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!