:remote => true confusion with form_for

▼魔方 西西 提交于 2021-02-07 10:14:56

问题


I'm just learning Rails and would appreciate any help. I've looked at probably 10 tutorials for :remote => true on form_for and they all seem to copy each other (equally confusing).

This tutorial is one of them: http://tech.thereq.com/post/18910961502/rails-3-using-form-remote-true-with-jquery-ujs

My web app is pretty much all on one page, and my 'submit form' is inside a pop-up lightbox. This means that the form_for code is actually within my index.html.erb. It also means a new @playlist instance variable is created for index (instead of having this be in new)

My end goal is this:

if @playlist.save returns true, I want to REDIRECT the user to the root_path WITH a :notice. There is no need to update the form with a success message or anything like that. I want a hard redirect on save success.

if @playlist.save returns false, I want to render the code inside create.js.erb, which will append the errors into the lightbox form without refreshing the page. note A lot of the tutorials seem to want to re-render the entire form as html, then replace the contents in place. I'd rather (if I can), just send the errors to the form and insert them, I think re-rendering and replacing the entire form seems more complicated than it has to be.

As of right now, my code is kind of muddled, especially:

  • the respond_to block. This is the largest point of confusion about what actually goes here, I have tried lots of different code and nothing works
  • I have no idea if create.js.erb is correct
  • I have no create.html.erb - I assume this is right because I just want to redirect

First line of my form:

<%= form_for @playlist, :remote => true do |f| %>

My entire PlaylistsController

class PlaylistsController < ApplicationController

  before_filter :load_genres, :only =>[:index, :user]
  before_filter :new_playlist, :only => [:index, :new, :create]

  def index
    @playlists = Playlist.order('created_at DESC').page(params[:page]).per(30)
  end

  def new
  end

  def create
    respond_to do |format|
      if @playlist.save
        # ???
        format.html { redirect_to root_path, :notice => "Playlist submitted" }
      else
        # ???
        format.js { render :js => @playlist.errors, :status => :unprocessable_entity }
      end
    end
  end

  def user
    @playlists = Playlist.order('created_at DESC').where(:username => params[:username]).page(params[:page]).per(30)
  end

  def listen
    playlist = Playlist.find(params[:playlist_id])
    playlist.update_attribute(:listens, playlist.listens + 1)
    render :nothing => true
  end

  private

  def load_genres
    @genres = Genre.order(:name)
  end

  def new_playlist
    @playlist = Playlist.new(:title => params[:title], :url => params[:url], :description => params[:description])
  end

end

create.js.erb

jQuery(function($) {
    alert('createjs called');
    $('#submit-form form').on('ajax:error', function(event, xhr, status, error){

        var responseObject = $.parseJSON(xhr.responseText), 
            errors = $('<ul />');

        $.each(responseObject, function(index, value){
            errors.append('<li>' + value + '</li>');
        })

        $(this).find('.submit-playlist-errors').html(errors);
    });
});

回答1:


If I understand you correctly, you have a form and you want to redirect with flash notice if the form is saved. If the form is invalid you want do display errors.

You can send the form (or any request) to rails app in different formats (usually html, json and js). This is specified in the url you provides.

playlist_path(@playlist)
# => /playlists/1

playlist_path(@playlist, :format => "json")
# => /playlists/1.json

playlist_path(@playlist, :format => "html")
# => /playlists/1.html
# ... etc

The format tells rails which kind of response specified in your respond_to block to use.

Now if this is clear you have to decide which format to use.

If you specify js format, the response will be treated as script an executed. If the form is valid, you can send user javascript that will redirect them to the page you desire. If the form is invalid, you can modify HTML in the way so you can display error messages. No client-side code needed.

respond_to do |format|
  if valid
    format.js { render "valid" }
  else
    format.js { render "invalid", :status => :unprocessable_entity }
  end
end

# valid.js.erb
window.location = <%= your_path %>

# invalid.js.erb
// whatever to display errors

When you use json you can return redirect url to redirect user or errors if the form is invalid. Note that this solution requires Javascript on the client side to handle it.

# controller
respond_to do |format|
  if valid
    format.json { render :json => your_path }
  else
    format.json { render :json => @object.errors, :status => :unprocessable_entity }
  end
end

# client side javascript
$("form")
  .on("ajax:success", function(event, data, status, response) {
    window.location = data
  })
  .on("ajax:error", function(event, response, error) {
    // logic to handle errors
  })

You can also use html format (by setting format to html or not specifying format at all). But if you use :remote => true you will have to treat it the same way as json (have client side javascript). If you use redirect_to as a response, it will redirect the request, not your user.

I am not sure about displaying the flash message after the user is redirected but you can try it this way:

respond_to do |format|
  if valid
    format.html { 
      flash[:notice] = "Playlist submitted"
      render :json => your_path }
  else
    format.html { render :json => @object.errors, :status => :unprocessable_entity }
  end
end

# client side javascript
//...

Hopefully this will dry your respond_to block.



来源:https://stackoverflow.com/questions/12568887/remote-true-confusion-with-form-for

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!