The rest of this series can be found here.
Over the weekend, I worked on this project on my wife’s laptop instead of my main machine, so ran into a couple of issues getting set up. First, rails wouldn’t run, but $ bundle install
took care of that problem. Then I had to run $ rake db:migrate
to get the database set up, but ran into similar issues as yesterday. At some point, I changed the name of the constant used to add the boolean field, but the migration file still had the old class name. Once I changed that, $ rake db:migrate
ran just fine.
Next, I needed to add a few todo items to have some data to work with. For whatever reason, adding new todos worked fine on my main machine, but when I tried to add a new item, I was getting a “First argument in form cannot contain nil or be empty” error. Turns out I had never defined the “new” action in the controller. Took care of that and the app was back in business.
Adding some features
- It only makes sense to be able to create and delete items from the main list of to-do items, so added that feature.
- It also makes sense to sort the list into “open” vs. completed to-do items; time for a crash-course in Ruby conditional syntax—turned out not to be a very hard crash.
- It also makes sense to have a one-click “complete” button on the list of items, but that is a bit tricky. The current method (going to the edit screen for that item) is
a bitclunky.- My first thought was to build a `<form>` in the actions column for each item, complete with an `<input type=”hidden” name=”completed” value=”true”>` element, but realized that it would likely clear out the the other two fields.
- My next thought was to add hidden inputs for the title and notes fields, but then the app is printing a bunch of hidden content, plus I’d have to come back and update the form here every time I added or changed fields. I possibly could extend the `_form.html.erb` partial for use here, but there’s still the “bunch of hidden unnecessary content” issue.
- Time to try writing a custom route and controller action to update just the one field from a one-click button. Took some working through to get the syntax just right; more info below.
Custom Route and Controller Action
To accomplish the one-click update, I needed a new route pointing the PATCH method at a new controller action that would update the completed
field. Here’s what I eventually came up with:
patch 'todos/:id', to: 'todos#complete'
: a new route that points the patch
method to the complete
action.
Speaking of the complete
action in todos_controller.rb
, here it is:
def complete
@todo = Todo.find(params[:id])
if @todo.update(completed: true) #updates just the completed field and ignores the rest
redirect_to todos_path
end
end
The only other change was updating the one-click link path and method: link_to '✓', todo_path(todo), method: :patch
One more enhancement
If we can have one-click completion, why not one-click “uncompletion” as well? No reason not to….
The only changes necessary were updating the link_to
in the completed section and adding a wrapping condition to the complete
action; it tests whether completed
is true or false and toggles it.
At this point, I also added a backup: if the @todo.update
call fails, then the app will render the edit screen, where the change can be manually made.
Screenshot
Just for fun, this is how the app looks right now:
Wrap-up
I haven’t made as much progress the last few days as I had hoped, but ran into some interesting and fun challenges today.
Code
The code is located on GitHub here.