Rails Ajax更新div

Hi i'm new to rails and i want to move a user story in its correct column depending on its state (to be done, in progress,..) with ajax my show template contains four divs with classed (ToBeDone, InProgress, ToBeVerified, Completed)

my update.js.erb

$("div.<%= @user_story.state.underscore %>:first").append("<%= j render(@user_story)%>");

and my update action

 if !@user_story.users.include? current_user
      @user_story.users << current_user
    end
    format.html { redirect_to @user_story.project, notice: 'User story was successfully updated.' }
    format.json { render :show, status: :ok, location: @user_story }
    format.js

and my user_story partial (this form is submitted when the user selects state)

<%= form_for [@project, user_story], remote: true  do |f|%>
  <div class="field" id='my_select_chzn'>
    <%= f.label :state %>
    <%= f.select :state, ['To be done', 'In progress', 'To be verified', 'Completed'], {:selected => user_story.state }, :id =>'select-state', :class => 'chosen-select'%>
  </div>
<%end%>

Please help, thanks and sorry for this long post.

Several things you'l benefit from:


Ajax

When you mention you're using "ajax", you must remember that ajax has to be called. You currently are using the remote: true functionality (totally fine), but also mention that you want to call it when you change the select box.

The problem here is that remote: true is rails UJS, and is scripted to only fire when an "action" has been triggered on your element. In short, for forms, it will only trigger when you submit the form.

This means you have to ensure the ajax actually fires:

#app/assets/javascripts/application.js
$(document).on("change", "#select-state", function() {
   $(this).parent().submit();
});

#app/views/controller/view.html.erb
<%= form_for [@project, user_story], remote: true  do |f|%>
  <div class="field" id='my_select_chzn'>
    <%= f.label :state %>
    <%= f.select :state, ['To be done', 'In progress', 'To be verified', 'Completed'], {:selected => user_story.state }, :id =>'select-state', :class => 'chosen-select'%>
  </div>
<% end %>

This should trigger the Ajax request for you. If you want to learn more about Ajax, just comment me on here!


DIV

To update your div, you need to be able to capture the Ajax response.

This can be done in two ways: by capturing with JS, or by using the Rails UJS mechanisms (which is what you're doing). When you call update.js.erb, you're creating the JS which you'd like to be run on your page

This means you may want to do something like this:

#app/controllers/your_controller.rb
...
  def update
    @user_story.users << current_user unless @user_story.users.include? current_user
    respond_to do |format|
       format.js
       format.html { redirect_to @user_story.project, notice: 'User story was successfully updated.' }
       format.json { render :show, status: :ok, location: @user_story }
    end
  end

Then, in your .js.erb file, you'll want to make sure your div elements are present for your JS to work on. I see you're calling $("div.<%= @user_story.state.underscore %>:first") - you need to ensure this div actually exists.

Moreover, I'd actually say that having 4 separate divs is bad. In keeping the DRY principles at the core of Rails, you'll be best keeping a single div, populating with the data you want & styling with different CSS classes


State Machine

Your use of "state" is almost the picture-perfect implementation of a state_machine.

This is a type of programming functionality (I believe used in electronics too), to give your objects state - meaning that instead of having to set a series of "strings" for your state column, you should be able to give it object orientated functionality

I believe using one of the state machine gems for Rails will give your application a much deeper and more thorough implementation of your desired functionality:

All of these (from my experience) work in a very similar way -- they update a central state column with relative data you set in your state machine. The major difference is that you'll be dealing with things on an object / model level, rather than setting a string.

It will be easier to understand if you look through the state_machine documentation, especially pertaining to the methods you get from implementing one of these gems. This should demonstrate the object-orientated nature of the state of your model - which you can implement below:

#app/models/user_story.rb
class UserStory < ActiveRecord::Base
  include AASM

  aasm do
    state :to_be_done, :initial => true
    state :in_progress
    state :to_be_verified
    state :completed

    event :complete do
      transitions :from => [:to_be_done, :in_progress, :to_be_verified], :to => :completed
    end

    event :progress do
      transitions :from => [:to_be_done, :to_be_verified, :completed], :to => :in_progress
    end

    event :verify do
      transitions :from => [:to_be_done, :in_progress, :completed], :to => :to_be_verified
    end
  end
end

This will give you the ability to call the various methods which attach themselves to the UserStory object:

@user_story = UserStory.find 1

@user_story.state # -> "to be verified"
@user_story.completed? #-> true / false
@user_story.in_progress? #-> true / false

@user_story.complete! # -> state = "completed"

Now, this won't help you directly in setting a "state" for your object. The only way this will really help is if you structure your object creation around the various methods you'll get as a result of using the state machine

A good example of this would be if you wanted to send a "bulk" email - you'd be able to use the following:

#app/models/message.rb
class Message < ActiveRecord::Base
   after_create :send

   private

   def send
      self.broadcast!
   end
end

If you'd like more information about this, please let me know in the comments!