[ToDoList] More Front-end CRUD
Our application can now create new entries into our database and allow users to read them: by the reckoning of the CRUD principles, we have successfully created half of an application! But supposing a user entered a typo in their original submission of a To-Do item, or needed to add something to its description; this is where the update part of CRUD comes in, which we can achieve by adding functionality to edit to our app in similar fashion to how we've developed the existing functions.
The routes for this are already set up via our magic line in the routes configuration.
Consulting the routes table we can see that the two actions we need to create are
todos#edit
and todos#update
,
which will present a form to edit a Todo
object and write this change to the DB table respectively.
Let's begin by adding these to the TodosController
(/app/controllers/todos_controller.rb
)
- make sure this goes above the private
block at the bottom:
def edit
@todo = Todo.find(params[:id]) ## find Todo to edit from the ID in browser path
end
def update
@todo = Todo.find(params[:id]) ## find the item
if @todo.update(todo_params) ## update DB entry
flash[:notice] = 'Your To-Do item was successfully updated!'
redirect_to todo_path(@todo) ## redirect to show view if successful
else
render 'edit' ## show same form w/ errs if update unsuccessful
end
end
Now you may notice that these two methods are extremely similar to the #show
and #create
methods respectively
- in fact the code for #show
and #edit
are identical!
There is a bit more of a difference between the if
blocks in #update
and #create
,
but we are still retrieving the Todo
object in exactly the same way.
Bravo to you if you have questioned this and wondered if there is a way we can not just repeat code like this - we most certainly can, and we will do so when we refactor our code in a later section.
With our controller equipped with instructions on what to do, we can now create our edit
view.
As we've already realised, this will essentially be exactly the same interface as creating a new Todo
entry,
so we can save ourselves some work and simply copy the new view file into a separate edit view file:
$ cp app/views/todos/new.html.erb app/views/todos/edit.html.erb
And now all we need to do is change the heading at the top of the page to reflect this new functionality:
<h3> Edit To-Do item: <%= @todo.id.to_s %> </h3>
Again, having identical code in two different files isn't the most optimal/efficient way of achieving this,
but we will cover this as well when we refactor later on.
For now, we can navigate to our edit page in the browser with a Todo id
in the URL,
and can see our new functionality in action!
(i.e. http://host/todos/1/edit).
By now I'd imagine there are quite a few To-Do items sitting in your database table,
and quite frankly it's a pain having to guess their ID to reach them in the browser.
Looking in our routes table we can see that our magic line in the config has allowed Rails has preempt this,
and it has set up a /todos
route for us that points to todos#index
.
Not wanting to waste all of the hard effort Rails has gone to to make this easy for us,
let's therefore add this action into our TodosController
(again, above the private
section at the bottom):
def index
@todos = Todo.all ## simply grab all of the Todo items
end
This will simply pass the array of all of our Todo
objects to a view using the variable we have cited (@todos
),
so let's create an index
view to present this list to our users in a nice table.
Because we are nice app developers who want a happy experience for our users,
let's also utilise Rails' handy link_to
helper to provide links to the
show
and edit
views for each of the To-Do items that we list.
We will need to reference the prefix values from our routes to do so (not forgetting the _path
suffix)
and then pass the Todo
object so that the helper can extract the id
.
This sounds tricky I know, but Rails won't let us down. Let's create /app/views/todos/index.html.erb
and fill it with:
<div align="center">
<h3> Your To-Do items: </h3>
<table border=1> <!-- create table with a border -->
<tr>
<th> ID </th> <!-- give the table appropriate headers -->
<th> Name </th>
<th> Description </th>
</tr>
<% @todos.each do |todo| %> <!-- array passed by controller -->
<tr> <!-- new row per Todo object -->
<td> <%= todo.id.to_s %> </td> <!-- insert values of object -->
<td> <%= link_to todo.name, todo_path(todo) %> </td> <!-- link name to #show -->
<td> <%= trunacate(todo.description, length: 100) %> </td>
<td> <%= link_to 'Show', todo_path(todo) %> </td> <!-- pass todo var as arg -->
<td> <%= link_to 'Edit', edit_todo_path(todo) %> </td> <!-- #edit view link -->
</tr>
<% end %> <!-- end the each block -->
</table> <!-- close off the table -->
</div>
Notice that we used the truncate
helper on the description and passed it the number of characters we want to limit the visible field to,
so that it won't completely take over our page if there is a large description.
If we now navigate to http://host/todos this will display a lovely table of all the valid Todo
entries in the database
(a To-Do list, if you will), with links to show
them in full or edit
them via a form.
We have also been extra helpful and got the To-Do item's name to link to the show
view!
While we are on the subject of making the site a bit more user-friendly,
you might notice that we are missing a few links that would really make site navigation a lot easier.
Let's get this done now, starting with an link in our main nav bar to view our To-Do list in /app/views/layouts/application.html.erb
:
| <%= link_to 'My To-Do List', todos_path %> <!-- use route prefix and _path -->
From the index
view, let's also have a link below the table to quickly create a new To-Do item. However let's make this a bit more fancy and put it in a button!
The only complication here is that, by default, Rails will make button actions use POST instead of GET,
but that's easily circumvented with a :method
flag. Some would argue that buttons shouldn't be used this way, but I say no beauty without pain.
So, below the table but still within the
div
, let's add our button in /app/views/todos/index.html.erb
:
<p> <%= button_to 'Create new To-Do item', new_todo_path, method: :get %> <p>
It's looking a lot better already, right?
Let's also deal with the views linked to from within in our table,
and add options on the show
view that can edit the displayed Todo
as well as go back to the list
- in /app/views/todos/show.html.erb
(below the name and description):
<p>
<%= link_to 'Edit this item', edit_todo_path(@todo) %> <!-- pass obj from controller -->
| <%= link_to 'Back to To-Do list', todos_path %> <!-- don't need to pass obj -->
</p>
Now let's do the equivalent on the edit
view,
substituting the Edit link for a Show link - /app/views/todos/edit.html.erb
:
<p>
<%= link_to 'Back to To-Do item', todo_path(@todo) %> <!-- #show link -->
| <%= link_to 'Back to To-Do list', todos_path %> <!-- #index link -->
</p>
And, to round it off, we'll also add an option to go back (or Cancel) on the new
view
- /app/views/todos/new.html.erb
:
<p> <%= link_to 'Cancel', todos_path %> </p>
Go back to your browser to test all of your lovely new links (if you haven't already),
and feel free to take the opportunity to commit and push your changes to GitHub.
Comments
Post a Comment