Getting Started with Ember.js Part 3: Showing a Single Car
In the last post, we set up the index view. Let’s set up a route that shows and saves a single car’s details.
Getting Started with Ember.js
- Part 1: Project Setup -> Github: one two three
- Part 2: The List View -> Github
- Part 3: Showing a Single Car -> Github
- Part 4: Creating a New Car -> Github
- Part 5: Deleting a Car and Wrapup -> Github
I’m combining the show and edit view here since they are largely redundant. Using the show view, we’ll need to have a save button that takes the model and calls the server to save the model. We’ll also add a cancel button that returns the user back to the index route.
Add the show route to the router in app/assets/javascripts/router.js
. Note the inclusion of the :id parameter. This gives the route URL a parameter like /#/cars/4,
where 4 is the primary key for the car.
Cars.Router.map(function() { this.resource('cars', function() { this.route('show', { path: '/:id' }); }); });
Add the Ember Data call to the cars route in app/assets/javascripts/routes/CarsRoutes.js
. When this route is triggered, Ember Data will make a get request to /cars/:id to get the list of cars to display. It should look like this:
Cars.CarsIndexRoute = Ember.Route.extend({ model: function() { return this.store.findAll('car'); } }); Cars.CarsShowRoute = Ember.Route.extend({ model: function(params) { return this.store.find('car', params.id); } });
Add the show template in app/assets/javascripts/templates/cars/show.js.hbs
. This is using the Foundation grid for formatting. All of the divs for formatting aren’t strictly necessary, but will align all of the fields. The more important bits are the Handlebars helpers like {{input}}.
<div id="cars-show" class="panel"> <h1>Edit Car</h1> <form {{action 'save' model on="submit"}}> <div class="row"> <div class="small-3 columns"> <label for="make" class="right inline required">Make</label> </div> <div class="small-9 columns"> {{input id="make" type="text" required="" value=make}} </div> </div> <div class="row"> <div class="small-3 columns"> <label for="model" class="right inline required">Model</label> </div> <div class="small-9 columns"> {{input id="model" type="text" required="" value=model.model}} </div> </div> <div class="row"> <div class="small-3 columns"> <label for="color" class="right inline required">Color</label> </div> <div class="small-9 columns"> {{input id="color" type="text" required="" value=color}} </div> </div> <div class="row"> <div class="small-3 columns"> <label for="condition" class="right inline required">Condition</label> </div> <div class="small-9 columns"> {{input id="condition" type="text" required="" value=condition}} </div> </div> <div class="actions"> {{#link-to 'cars.index' class='button alert' }} Cancel {{/link-to}} </div> </form> </div>
If you visit localhost:3000/#/cars/1 in the browser, you should see that the route throws a 404 error in the console. This is because we haven’t set up rails to handle the show action yet. So let’s take a break and do that. Add the typical set_car and car_params methods. Also add a show method that renders json using the serializer. It should look like this now:
class CarsController < ApplicationController before_action :set_car, only: [:show] def index respond_to do |format| format.json { render json: Car.all } end end def show respond_to do |format| format.json { render json: @car } end end private def set_car @car = Car.find(params[:id]) end def car_params params.require(:car).permit(:id, :make, :model, :color, :condition) end end
You should be able to get the car in JSON form at localhost:3000/cars/1.json now. Refresh the view, and it should be populated with some data.
Now back at the index view in app/assets/javascripts/templates/cars/index.js.hbs
, you can now link to the show view in the grid. Replace one of the properties with a link-to:
<div id="cars-index" class="panel"> <h1>Cars</h1> <table role="grid"> <thead> <tr> <th>Make</th> <th>Model</th> <th>Color</th> <th>Condition</th> </tr> </thead> <tbody> {{# each car in model}} <tr> <td>{{#link-to 'cars.show' car.id}} {{car.make}} {{/link-to}}</td> <td>{{car.model}}</td> <td>{{car.color}}</td> <td>{{car.condition}}</td> </tr> {{/each}} </tbody> </table> </div>
Now let’s add a save button. As far as I can tell, it’s idiomatic to put the save action on the route and not the controller like you might expect. So we’ll add a save action to the Cars show route.
Add a save button that submits the form and add an action-on-submit to the form. show.js.hbs should now look like this:
<div id="cars-show" class="panel"> <h1>Car</h1> <form {{action 'save' model on="submit"}}> <div class="row"> <div class="small-3 columns"> <label for="make" class="right inline required">Make</label> </div> <div class="small-9 columns"> {{input id="make" type="text" required="" value=make}} </div> </div> <div class="row"> <div class="small-3 columns"> <label for="model" class="right inline required">Model</label> </div> <div class="small-9 columns"> {{input id="model" type="text" required="" value=model.model}} </div> </div> <div class="row"> <div class="small-3 columns"> <label for="color" class="right inline required">Color</label> </div> <div class="small-9 columns"> {{input id="color" type="text" required="" value=color}} </div> </div> <div class="row"> <div class="small-3 columns"> <label for="condition" class="right inline required">Condition</label> </div> <div class="small-9 columns"> {{input id="condition" type="text" required="" value=condition}} </div> </div> <div class="actions"> <button type="submit" class="button success">Save</button> {{#link-to 'cars.index' class='button alert' }} Cancel {{/link-to}} </div> </form> </div>
Back in app/assets/javascripts/routes/CarsShowRoute.js
add an action object with the Ember Data code that will save the model. Note that you can transition back to index after success, or you could stay on the same page and display some kind of success message.
Cars.CarsShowRoute = Ember.Route.extend({ model: function(params) { return this.store.find('car', params.id); }, actions: { save: function(car) { var self = this; car.save().then(function(response) { //you can transition back to index //self.transitionTo('cars.index'); }).catch(function() { console.log('Failure!') }); } } });
Now if you try to save you should get another 404 put error since we haven’t added the update part to Rails. Let’s do that now.
Add an update method and add :update to the before filter. The controller should now look like this:
class CarsController < ApplicationController before_action :set_car, only: [:show, :update] def index respond_to do |format| format.json { render json: Car.all } end end def show respond_to do |format| format.json { render json: @car } end end def update respond_to do |format| if @car.update(car_params) format.json { render json: @car, status: :ok } else format.json { render json: @car.errors, status: :unprocessable_entity } end end end private def set_car @car = Car.find(params[:id]) end def car_params params.require(:car).permit(:id, :make, :model, :color, :condition) end end
Head back to the form and you should be able to save the update. Check the console for the log messages and check the index page to see if the save worked.
Now that you’ve got the show view working and saving, head on to Part 4: Creating a New Car.
No Comments
No comments yet