Giter VIP home page Giter VIP logo

ember-routing-dynamic's Introduction

General Assembly Logo

Ember Dynamic Routing

Prerequisites

Objectives

By the end of this, developers should be able to:

  • Set up resource routes.
  • Create a Route object.
  • Configure a Route to parse information from a URL.

Preparation

  1. Fork and clone this repository.
  2. Install dependencies with npm install and bower install.
  3. Run the development server with ember server. Use the --proxy flag to avoid writing development-specific CORS and CSP settings.

Code-Along: Routing to Resources

In the previous lesson on Ember routing, you learned how to generate templates and connect to them via the Router.

Routes linking to (basically) static HTML are well and good, but most of the time we're interested in showing and manipulating data from resources (e.g. products). Although the routes for resources used to be distinct from normal routes, in Ember 2 that distinction has disappeared - now, a route for a resource (such as products) looks like any other route.

// app/router.js

Router.map(function() {
  this.route('products');

  this.route('about');
  this.route('contact');
  this.route('team', function(){
    this.route('leadership');
    this.route('engineering');
    this.route('sales');
  });
});

However, in order for such a route to actually have data to load, we need to create a Route object. As you may recall from the Ember Overview lesson, the purposes of the Route object are (1) to parse the URL for a given route, and (2) to use information from that URL to load model data.

Generating this Route object is fairly easy. In the case of a products route, we can do this by running the command ember g route products; this will create two new files in the app/products directory, a Route file and a Template file.

Let's take a closer look at that Route file.

// app/products/route.js

import Ember from 'ember';

export default Ember.Route.extend({
});

The way in which a Route file makes data available to a Controller, View, or Template is through a method that we define called model - this method returns some data object that gets used within the route. In this case, let us suppose that the model method returns an array of JavaScript objects, like so:

// app/products/route.js

import Ember from 'ember';

export default Ember.Route.extend({
  model: function(){
    return [
      {
        id: 1,
        name: 'Crock Pot',
        manufacturer: 'Farberware',
        price: 40
      },
      {
        id: 2,
        name: 'Food Processor',
        manufacturer: 'Cuisinart',
        price: 25
      },
      {
        id: 3,
        name: 'Electric Griddle',
        manufacturer: 'George Foreman Grills',
        price: 15
      },
    ];
  }
});

If we wanted to access this data from a Template, we can do so by referencing a property called model from within the Template, which points back to the result of the model function from the Route. Let's make a change to the products Template so that it shows the names of the products listed above.

<!-- app/products/template.hbs -->

<!-- {{outlet}} -->
<h2> Product Listings </h2>
<ul>
{{#each model as |product|}}
  <li>{{product.name}}</li>
{{/each}}
</ul>

{{#each set as |item|}} is a new construction for Ember 2. The pipes (|) play the same role here that they do in Ruby.

Lab: Routing to Resources

Create routes for each of the teams in our app: leadership, engineering, and sales. Each route should have a model hook that loads an example collection of employees on the team. Modify the appropriate templates to render the data from each model.

Routing with Dynamic Segments

Often, we don't just want to see the full list of a particular type of resource; we want to be able to zoom in on one in particular. Although it deals with the same type of resource, because it creates a different 'view state' than looking at the list as a whole, this must be represented with a separate route. Let's call this new route product, since it concerns zooming in on one product in particular from the list.

// app/router.js

Router.map(function() {
  this.route('products');
  this.route('product', {path: '/products/:product_id'})
  this.route('about');
  this.route('contact');
  this.route('team', function(){
    this.route('leadership');
    this.route('engineering');
    this.route('sales');
  });
});

The object passed in as the second argument to the product route contains the actual path used to reach the product route. It's usually not necessary to specify the actual path, since the URL and the name of the route are usually the same; however, in this case, they are not the same, so we must specify the path explicitly.

The :product_id section of the path is called a dynamic segment; much like you've seen in Rails and Express, this part of the URL is a placeholder for a value, typically a number. The name :product_id is not special, and in fact we could have chosen any name for that segment.

As mentioned earlier, one of the two big responsibilities of the Route is to parse the URL and extract meaningful information - dynamic segments are the primary instance of this. Let's create a new Route for product (ember g route product).

// app/products/route.js

import Ember from 'ember';

export default Ember.Route.extend({
});

We still need a model for our Template; however, this time, rather than returning all products, we want to only return one, based on the value passed in as :product_id. As it turns out, though we usually ignore it, the model function normally accepts an object as an argument (typically called params) which holds data from that route's dynamic segment; if we extract that property from params (:product_id is stored at params.product_id), we can use it to look up the data we want.

// app/product/route.js

import Ember from 'ember';

export default Ember.Route.extend({
  model: function(params){
    return [
      {
        id: 1,
        name: 'Crock Pot',
        manufacturer: 'Farberware',
        price: 40
      },
      {
        id: 2,
        name: 'Food Processor',
        manufacturer: 'Cuisinart',
        price: 25
      },
      {
        id: 3,
        name: 'Electric Griddle',
        manufacturer: 'George Foreman Grills',
        price: 15
      },
    ][params.product_id - 1];
  }
});

Since our Route has a model method, we can now access the data from that method in the Route's corresponding Template.

<!-- app/product/template.hbs -->

<!-- {{outlet}} -->

<h2> Product Details </h2>
<h4>{{model.name}}</h4>
<h5>${{model.price}}</h5>
<p>Manufactured by <em>{{model.manufacturer}}</em></p>
{{#link-to 'products'}}Back to Product Listings{{/link-to}}

Now if we navigate to http://localhost:4200/products/3, we can now see information about the third product on our page.

Since this now works, let's make one final change: replacing the hard-coded HTML in the products template with {{#link-to}} helpers pointing to the specific pages for each product.

<!-- app/products/template.hbs -->

<!-- {{outlet}} -->

<h2> Product Listings </h2>
<ul>
  {{#each model as |product|}}
    <li>{{#link-to 'product' product}} {{product.name}} {{/link-to}}</li>
  {{/each}}
</ul>

Lab: Routing with Dynamic Segments

Let's make employee profiles. Create routes for showing individuals on each team. Those routes should pass employee IDs to the model hooks. You will also need a new template.

Bonus: Refactoring

Move model hook code into a service so that you can re-use it in different routes.

After that's done, the next challenge should be a bit simpler: create an employees route that shows all employees regardless of team, and change the team routes to filter all employees using a dynamic segment.

Additional Resources

  1. All content is licensed under a CC­BY­NC­SA 4.0 license.
  2. All software code is licensed under GNU GPLv3. For commercial use or alternative licensing, please contact [email protected].

ember-routing-dynamic's People

Contributors

ember-tomster avatar ga-meb avatar jrhorn424 avatar laurenfazah avatar payne-chris-r avatar

Watchers

 avatar  avatar

ember-routing-dynamic's Issues

Pass data *down*

Per @payne-chris-r (and I agree)
In objectives:

- -   Pass data from routes to components, and from components to components
+ -   Pass data down from routes to components, and from components to components

Archive this repo

When it is clear that it is fully merged with ga-wdi-boston/ember-routing

Use find to access elements of an array

Using params.product_id -1 only returns the correct product if everything is in perfect order. Using .find is better.

In product/route.js

export default Ember.Route.extend({
  model(params) {
    const id = params.product_id;

    const products = [
     {
       id: 1,
       name: 'Crock Pot',
       manufacturer: 'Farberware',
       price: 40
     },
     {
       id: 2,
       name: 'Food Processor',
       manufacturer: 'Cuisinart',
       price: 25
     },
     {
       id: 3,
       name: 'Electric Griddle',
       manufacturer: 'George Foreman Grills',
       price: 15
     },
   ];

   return products.find((product) => product.id === +id);
  }
});

Remove `tagName` option from `#link-to`?

@jrhorn424 argues that you shouldn't use it because "a link should always be a link".

I think there some situations in which that is not desirable - for instance, when dealing with a framework like Bootstrap. #link-to will only add an .active class to the specific element it creates, so if your links are items in a list and you want to highlight the list item that you've selected, the only way that this can be done is using tagName='li'

(See emberjs/ember.js#1822)

Code snippet is incorrect

<!-- {{outlet}} -->

<h2> Product Listings </h2>
<ul>
{{#each model as |product|}}
  {{#link-to 'product' product tagName='li'}}{{product.name}}{{/link-to}}
{{/each}}
</ul>

Since we're now nesting the product route within products, that #link-to should be

{{#link-to 'products.product' product tagName='li'}} ... {{/link-to}}

Show how to find context data

Context data is not available in the Data tab of Ember Inspector, because we're not using EmberData to load our data. It can be found in the context of the route by going to the Container tab (under Advanced) -> route -> click on route.
This is being moved from ga-wdi-boston/ember-routing#9

You can then see the context data on the right like so:
screen shot 2017-01-04 at 11 13 42 am

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.