Updated Tutorial: How Do I Update My Model from a checkbox?

Let’s say you have a Todo app, with a model Todo that has a string title, and a boolean field, completed. We are going to use Stimulus to update our model remotely on the server when we check off the todo. And we’re going to use a nifty JavaScript API called fetch() to make it all happen.

You can setup the Todo model with this command:

$ rails g scaffold Todo title:string completed:boolean --no-assets --no-helper --no-test-framework

So, in our Todos Controller, we’ll pull out all of our Todo records:

class TodosController < ApplicationController 
  def index
     @todos = Todo.all
  end
end

And in index.html.erb, we’ll display the Todos, and put in the decorations that Stimulus will use:

<table>
  <% @todos.each do |todo| %>
    <tr data-controller="todo" 
          data-todo-update-url="<%= todo_path(todo) %>">
      <td>
        <input type="checkbox" 
                    data-action="todo#toggle" 
                    data-target="todo.completed" 
                    <% if todo.completed %> checked <% end %> >
        <%= todo.title %>
      </td>
    </tr>
  <% end %>
</table>

Now, let’s create our todo_controller.js and add the toggle method we specified above:

import { Controller } from "stimulus"
export default class extends Controller {
    static targets = [ "completed" ]
    toggle(event) {
        // Code to follow
    }
}

Inside the toggle(event) function, start by getting the value of the checkbox, and put it into a FormData object:

let formData = new FormData()
formData.append("todo[completed]", this.completedTarget.checked);

Let’s post that data to the "update-url" value we set on the Todo row. We’ll set the method to PATCH so that it gets routed to our todo#update on our controller. The credentials and headers included ensure we send the session cookie and the CSRF protection token and prevent an ActionController::InvalidAuthenticityToken error.

fetch(this.data.get("update-url"), {
      body: formData,
      method: 'PATCH',
      credentials: "include",
      dataType: "script",
      headers: {
              "X-CSRF-Token": getMetaValue("csrf-token")
       },
})

We can take the Response object and verify that our request was successful. If there was an error, we’ll revert the checkbox change.

.then(function(response) {
    if (response.status != 204) {
        event.target.checked = !event.target.checked 
    }
})

Now, let’s go back to our ruby controller, and add our update method:

def update
  @todo = Todo.find_by_id params[:id]
  @todo.update todo_params
  @todo.save
  respond_to do |format|
    format.js #add this at the beginning to make sure the form is populated.
  end
end

private 

def todo_params
  params.require(:todo).permit(:title, :completed)
end

Now when we check or uncheck one of the check boxes, an AJAX request is sent to our Rails app, and Stimulus handles the response.

4 thoughts on “Updated Tutorial: How Do I Update My Model from a checkbox?

  1. Hi.

    Thanks for the tutorial – I followed your steps above exactly but I get an issue when checking the box:

    ReferenceError: Can’t find variable: getMetaValue

    I’m using Stimulus 2 if that makes a difference?

    I’ve googled getMetaValue but can’t find anything! any help appreciated as I have a project I’m working on with checkboxes in a table just like your example and cannot get the values to update when I click them either the “Rails way” via an AJAX remote: true method in a check_box_tag or via your method and its driving me nuts!

    Thanks.

  2. Hi – Always the way, you post a comment and then find the solution 10 minutes later!

    I’m assuming the getMetaValue function is one you wrote yourself and left out of the article to catch out lazy people like me ;-).
    Found the following link:

    https://guides.rubyonrails.org/working_with_javascript_in_rails.html#cross-site-request-forgery-csrf-token-in-ajax

    which gave me the answer:

    const token = document.getElementsByName(
    “csrf-token”
    )[0].content;

    I swapped out your getMetaValue function for the token variable after using the above and it now works great.

    Thanks again for the article – very useful.

    Mark

Leave a Reply