Forms: Editing Records Part 1
At this point we have some fundamental features under our belt and our movies app is shaping up nicely. The movies are neatly tucked away in a database and we can list and show them in the browser. That's a good start!
Now, what if we want to change a movie's details? The only way to do that currently is to fire up a console session and programmatically change movie attributes. But this is supposed to be a web app! So the next step is to create a web interface for editing (changing) movie details. We'll tackle this in two parts, similar to the way we did it in the video.
First we need to display a web form so a user can edit a movie's details. Doing that involves the following high-level tasks:
Add a route to handle requests for /movies/1/edit, for example.
Generate an "Edit" link on the show page.
Define an edit action in the MoviesController that finds the movie we want to update and displays an HTML form.
Create an edit.html.erb view template that generates the HTML form pre-populated with the movie's details.
Visually, here's what we want to do:
Then, in the second part, we'll need to define an update action in the MoviesController that saves movie changes to the database when the form is submitted.
We have our work cut out for us, so let's get started!
1. Add an Edit Route and Link
Let's start with the URL we want and work our way from the outside-in. By convention in Rails, the URL for editing a movie would be http://localhost:3000/movies/1/edit, for example. To make it easy to navigate there, we'll generate an "Edit" link on the show page.
As a jumping-off point, browse to http://localhost:3000/movies/1/edit and you should get the following error:
Routing Error No route matches [GET] "/movies/1/edit"
Fix it by adding a route that sends requests for movies/1/edit, for example, to the edit action of the MoviesController. Make the route flexible enough to recognize any arbitrary movie id (not just 1). We'll need to be able to generate an "Edit" link, so go ahead and name the route edit_movie.
Now list the defined routes and you should have the following four routes, with the new route listed last:
Helper HTTP Verb Path Controller#Action root_path GET / movies_path GET /movies(.:format) movies#index movie_path GET /movies/:id(.:format) movies#show edit_movie_path GET /movies/:id/edit(.:format) movies#edit
Great! Now use the route helper method to generate an "Edit" link at the bottom of the show template. Once you get the link working, go ahead and copy in the version in the answer that uses HTML elements and class names that trigger the styles in our custom.scss stylesheet.
Finally, hop back into your browser, navigate to a movie's show page, and click the newly-generated "Edit" link. You should get the following error:
Unknown action The action 'edit' could not be found for MoviesController
Clearly we need to implement the edit action next.
Before moving to the next step, think about how you'll do it! It's good to start internalizing the high-level steps before jumping right to the code.
2. Create the Edit Action and Form
Did you figure it out? The edit action needs to do two things. First, similar to the show action, the edit action needs to use the ID embedded in the URL to find the appropriate movie in the database. Then the action needs to render a form and populate the form fields with the respective movie's attributes.
First up, define an edit action in the MoviesController that finds the requested movie and assigns it to an instance variable named @movie.
Refresh the page (you're still accessing http://localhost:3000/movies/1/edit) and you should totally expect this error:
No template for interactive request MoviesController#edit is missing a template...
Following the error, create a file named edit.html.erb in the
app/views/moviesdirectory. Inside that file, start by simply displaying the movie's title as a quick sanity check that the correct movie is being fetched from the database.
Refresh and you should see the movie title. So far, so good!
Now that we have a movie object in the template, let's start incrementally working on the form. First, update the edit.html.erb template to generate a form using the form_with helper method. For now, just add a label and a text field for the movie's title attribute.
Refresh and you should see a label and a text field populated with the the movie's title.
Then incrementally add form elements so you can edit a movie's description, rating, released on date, and total gross. You'll need to generate the appropriate HTML form element depending on the type of attribute:
- description (a text attribute) goes in a text area with 7 rows, for example
- rating (a string attribute) goes in a text field
- released_on (a date attribute) goes in a date select with the HTML option class set to "date".
- total_gross (a decimal attribute) goes in a number field
As you work through these you might find it helpful to use the documentation at http://api.rubyonrails.org/. For example, search for "text_field" and "date_select" to find the corresponding form helpers.
When you're done, refresh the form and all the fields should contain the values for the attributes in the @movie object. In other words, the values in the form should reflect the movie details that are in the database.
Finally, add a submit button at the bottom of the form:
Refresh and you should see an "Update Movie" button. Wait a minute! How did Rails know that the submit button should say "Update Movie"? It figured that out because the @movie object you passed to form_with is an existing record in the database, so you must be trying to update it. Pretty clever, eh?
Bonus: As an alternative to using the date_select form helper, you can instead use the date_field helper. This helper creates an input of type "date", which in the Chrome browser will show a little triangle next to the input box. Click the triangle, and a calendar pops up so you can easily pick a date! Note that not all browsers support the HTML 5 "date" type.
It's tempting to want to submit the form, but that's the challenge of the next exercise. So if you're seeing a form populated with a movie's data, then you're good to go for this exercise!
The full solution for this exercise is in the
forms-edit-1 directory of the code bundle.
Excellent! Now we're showing a form for changing a movie's details. Next we need to handle what happens when a user submit the form...