14

I am new to rails and trying to change the value of a boolean via a checkbox and using jquery ajax:

<%- @tasks.each do |task| %>
  <div class="task-wrapper">
    <%= check_box_tag 'completed', task.id , task.completed, :class => "task-check" %>
    <%= content_tag :span, task.task %>
    <%= content_tag :span, task.deadline %>
  </div>
<% end %>

and the javascript:

$(".task-check").bind('change', function(){
  if (this.checked){
    var bool = this.checked ? 1 : 0;
    $.ajax({
      url: '/todos/toggle',
      type: 'POST',
      data: '{"task_id":"'+ this.value +'", "bool":"'+ bool +'"}'
    });
  }
  else {
    alert("no");
  }
});

then the controller:

def toggle(task_id, bool)
  @task = Todo.find_by_id(task_id)

  if @task != nil?
    @task.update_attributes(:completed => bool)
  else
    set_flash "Error, please try again"
  end
end

finally the routes:

resources :todos do
  member do
    post 'toggle'
  end
end

also tried collection but gives the same error.

when ever i try it i get a 404 error on the action.

what is the problem?

thanks

4
  • Is your task loop inside a form? Commented Mar 22, 2013 at 14:23
  • @Catfish - no its outside the form Commented Mar 22, 2013 at 14:24
  • 1
    Why don't you put it inside a form? It'd be much simpler. Then instead of check_box_tag you can use f.check_box. Commented Mar 22, 2013 at 14:34
  • @Catfish - But how would i change the bool without a postback? Commented Mar 22, 2013 at 14:49

2 Answers 2

45

As of Rails 4, there's a way to do this without needing any additional JS or CSS:

<%= check_box_tag 'completed', task.id, task.completed,
      data: {
        remote: true,
        url: url_for(action: :toggle, id: task.id),
        method: "POST"
      } %>

It turns out that adding remote: true to an input causes jquery-ujs to make it ajax-y in all the nice ways. Thoughtbot's "A Tour of Rails jQuery UJS" briefly touches this (and many other good things available); the "Unobtrusive scripting support for jQuery" page in the jQuery UJS wiki does a thorough job on this as well.

Sign up to request clarification or add additional context in comments.

2 Comments

Also available for earlier versions of rails, not exactly sure which ones, but last version of 3 works.
Still working for rails 5.1.6 and 5.2. This is the best awnser here.
16

Try the following (leaving everything else as is):

the javascript:

$(".task-check").bind('change', function(){
  if (this.checked){
    $.ajax({
      url: '/todos/'+this.value+'/toggle',
      type: 'POST',
      data: {"completed": this.checked}
    });
  }
  else {
     alert("no");
  }
});

the controller:

def toggle
  @task = Todo.find(params[:id])

  if @task.update_attributes(:completed => params[:completed])
    # ... update successful
  else
    # ... update failed
  end
end

Take a look at bundle exec rake routes to show you the paths that rails generates. In the case of your post 'toggle' which is a member you get a path like /todos/:id/toggle, hence the updated url in the ajax.

In the controller the :id from the path ends up in params[:id]. The data from the ajax request also ends up in the params hash, hence params[:completed].

2 Comments

This works great, could you please explain why it does? Thanks
Note that Rails 4 adds a simpler way, as noted below

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.