Working with relationships

When working with a data store, you'll need to be able to handle relationships. In this recipe, we'll go over some common relationships from one-to-many and many-to-one and also how to use it with Ember Data.

Getting ready

As with the other recipes, we'll be using Ember CLI Mirage to mock our backend. In this recipe, we'll create a simple one-to-many and many-to-one relationship. We'll mock a school that has instructors and classes. For every class, there is one instructor. Every instructor will have one or more classes.

  1. Create a new Ember application. In this application, generate the following files:
    $ ember install ember-cli-mirage
    $ ember g model instructor
    $ ember g model class
    $ ember g route index
    $ ember g helper addone
    $ ember g adapter application
    $ ember g fixture classes
    $ ember g fixture instructors
    

    This will create the models, route, adapter, and helper that we'll need for this application.

  2. In the mirage fixtures folder, update these two files, classes.js and instructors.js:
    // app/mirage/fixtures/classes.js
    export default [
        {id: 1, subject: 'History',instructor:[1]},
        {id: 2, subject: 'Spanish',instructor:[1]},
        {id: 3, subject: 'Government',instructor:[3]},
        {id: 4, subject: 'English',instructor:[2]},
        {id: 5, subject: 'German',instructor:[2]},
        {id: 6, subject: 'Social Studies',instructor:[4]},
        {id: 7, subject: 'Math',instructor:[]}
    ];

    The classes.js file has a list of classes and subjects.

  3. Create the instructors.js file:
    // app/mirage/fixtures/instructors.js
    export default [
        {id: 1, name: 'John', age: 17, classes:[1,2]},
        {id: 2, name: 'Jack', age: 18, classes:[4,5]},
        {id: 3, name: 'Suze', age: 17, classes:[3]},
        {id: 4, name: 'Jane', age: 16, classes:[6]}
    ];

    As you can see, each young instructor has a list of classes that they teach. Each class has one, and only one, instructor for that class.

  4. Edit the config.js file for Mirage. Add the new routes:
    // app/mirage/config.js
    export default function() {
    
        this.get('/instructors',['instructors','classes']);
        this.get('/classes',['instructors','classes']);
    }
  5. Each one of these endpoints will return the instructor and class data. This is done via sideloading. Here is an example of a JSON response sideloaded:
    {
      "instructors": [
        {
          "id": 1,
          "name": "John",
          "age": "17",
          "classes": [1,2]
        },
        {
          "id": 2,
          "name": "Jack",
          "age": "18",
          "classes": [3,4]
    
        }
      ],
      "classes": [
        {
          "id": 1,
          "subject": "History",
          "instructor": [1]
        },
        {
          "id": 2,
          "subject": "Spanish",
          "instructor": [1]
    
        },
        {
          "id": 3,
          "subject": "Government",
          "instructor": [2]
        },
        {
          "id": 4,
          "subject": "English",
          "instructor": [2]
    
        },
      ]
    
    }

    As you can see from the preceding example, both the instructor and class data was returned. This is the default that RESTAdapter expects.

    On the other hand, we could return the data using asynchronous relationships. When this occurs, the server data store returns records only for one model. Ember then does one or more HTTP requests to retrieve data for the other model. For the simplicity of this example, we'll assume that the data is sideloaded.

  6. Finally, create a new scenario that loads the fixture data for us to use:
    // app/mirage/scenarios/default.js
    export default function( server ) {
    
      server.loadFixtures();
    }

    This will load both fixtures so that they can be returned to the Ember client.

How to do it...

  1. Open the application.js file in the adapter folder. Set it to RESTAdapter:
    import DS from 'ember-data';
    
    export default DS.RESTAdapter.extend({
    });

    RESTAdapter will be used for this recipe.

  2. Edit the class.js and instructor.js files in the models folder. Add the new properties for the model:
    // app/models/class.js
    import DS from 'ember-data';
    
    export default DS.Model.extend({
      subject: DS.attr('string'),
      instructor: DS.belongsTo('instructor')
    });

    In this example, we need to make sure that the class has one instructor. This can be accomplished using the DS.belongsTo method. This tells Ember to look at the instructor model for this property.

  3. The instructor has a one-to-many relationship with the class model. One instructor can have one or more classes that he teaches. We can accomplish this using the DS.hasMany() method and providing the name of the model:
    // app/models/instructor.js
    import DS from 'ember-data';
    
    export default DS.Model.extend({
        name: DS.attr('string'),
        age: DS.attr('number'),
        classes: DS.hasMany('class')
    });
  4. Update the index.js file in the routes folder. Specify it to return all the instructor data:
    // app/routes/index.js
    import Ember from 'ember';
    
    export default Ember.Route.extend({
        model(){
          return this.store.findAll('instructor');
        }
    });

    This route uses the Ember Data findAll method to return all the instructor data.

  5. Update the helper file:
    // app/helpers/addone.js
    import Ember from 'ember';
    
    export function addone(params) {
        return +params+1;
    }
    
    export default Ember.Helper.helper(addone);

    Helpers in Ember are used to manipulate template data. You can pass information to one and return information. In this example, we are doing some simple mathematics.

  6. Edit the index.hbs file with the model data:
    // app/templates/index.hbs
    {{outlet}}
    {{#each model as |instructor|}}
    Name of instructor: <b>{{instructor.name}}</b><br>
    Teaches Classes:<br>
    {{#each instructor.classes as |class index|}}
        <b>{{addone index}}: {{class.subject}}</b><br>
    {{/each}}
    <br>    <br>
    {{/each}}

    In this template, we are using the each helper to display the instructor's name. To access the class information, another each helper iterates over instructor.classes. In each iteration, we display the subject and index class. As the index starts at zero, we can pass it to the addone helper. This helper increments the number passed to it.

  7. Run ember server and you should see all the data displayed from the fixture data:
    How to do it...

    Each instructor is listed with each class

How it works...

Ember uses the DS.hasMany and DS.belongsTo methods to signify a one-to-many and a one-to-one relationship. Ember, by convention, assumes that you are using the JSON API adapter. At the time of writing this, the JSON API is the default adapter for Ember Data. It communicates with a server via well-defined JSON via XHR. Its goal is to be easy to work with on the client and server side while working with a broad set of use cases, including relationships. For the most part, the REST adapter works fine. So, I've included it in the book instead of the JSON API adapter. Be aware that you can use either to accomplish your goals.

This can be changed using RESTAdapter instead. RESTAdapter assumes that all keys are camel-cased and that any data sent is sideloaded. This is done to help developers easily integrate their backend APIs and data stores with Ember.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset