user2197158
user2197158

Reputation: 31

How should I be loading a related model into a template with Ember?

I've only just started using Ember and am struggling with a couple of concepts. I used Knockout for a previous application which was great, and I can already see how Ember will help me be more structured and write less code. Although I could go back to Knockout and get this up and running pretty quickly, I really want to persevere with Ember.

The bit I'm struggling with is the right way and place to load related records. My app has a list of systems, and a list of suppliers related to each system. Each system may have one or more suppliers, and vice-versa.

So far I have a route called Systems with a corresponding controller and template. The model hook on the route gets the list of systems from an API (I'm using Ember Data). The systems are displayed as a list via the template, and when the user selects one (via a radio button) I need to load and display the related suppliers. That's the bit I'm not sure about.

I'm not changing the URL here, just displaying an additional list of suppliers underneath the list of systems so adding a new route doesn't sound quite right. Conceptually what would be the best way to do this?

Here's the current code.

System Model

import DS from 'ember-data';

export default DS.Model.extend({
    name: DS.attr('string'),
    suppliers: DS.hasMany('supplier')
});

Supplier Model

import DS from 'ember-data';

export default DS.Model.extend({
    name: DS.attr('string'),
    systems: DS.hasMany('system')
});

Systems Route

import Ember from 'ember';

export default Ember.Route.extend({
    model: function() {
        return this.store.find('system');
    }
});

Systems Controller

import Ember from 'ember';

export default Ember.ObjectController.extend({
    systemSelected: false,

    actions: {
        selectSystem: function(id) {
            this.set('systemSelected', true);

            console.log(id);
        },
        selectSupplier: function(supplier) {
            console.log(supplier);
        }
    }
});

Systems Template

<h1>systems</h1>

<form id="systems">
    <fieldset>
        <div class="form-group">
            <label for="system" class="control-label">Select the system</label>
            {{#each}}
                <div class="radio">
                    <label>
                        <input type="radio" name="system" value="{{id}}" {{action 'selectSystem' id}}>
                        <span>{{name}}</span>
                    </label>
                </div>
            {{/each}}
        </div>

        {{#if systemSelected}}
            <div class="form-group">
                <label for="supplier" class="control-label">Select the supplier</label>
                {{#each suppliers}}
                    <div class="radio">
                        <label>
                            <input type="radio" name="supplier" value="{{id}}" {{action 'selectSupplier' supplier}}>
                            <span>{{name}}</span>
                        </label>
                    </div>
                {{/each}}
            </div>
        {{/if}}
    </fieldset>
</form>

Upvotes: 0

Views: 710

Answers (1)

NicholasJohn16
NicholasJohn16

Reputation: 2409

The way to handle this is pretty simple. You just load both related models in your route. You do that using RSVP.

First of all, we'll need a Suppliers Controller, but just a really basic one to store all the Suppliers:

App.SuppliersController = Ember.ArrayController.extend();

In your Systems Route, fetch the models like this and set them to the correct model properties:

import Ember from 'ember';

export default Ember.Route.extend({
    model: function() {
        return Ember.RSVP.hash({
           systems: store.find('system'),
           suppliers: store.find('suppliers')
        });
    },
    setupController(controller,models) {
        this.controllerFor('systems').set('model',models.systems);
        this.controllerFor('suppliers').set('model',models.suppliers);
    }
});

Then in your Systems Controller, add the Suppliers Controller to the needs:

import Ember from 'ember';

export default Ember.ObjectController.extend({
    needs:['systems'],
    systemSelected: false,

    actions: {
        selectSystem: function(id) {
            this.set('systemSelected', true);

            console.log(id);
        },
        selectSupplier: function(supplier) {
            console.log(supplier);
        }
    }
});

You might want to use an ArrayController instead of an ObjectController for Systems Controller since it stores more than one object.

Then in your Systems Template, you can access the Systems Controller as a property of your main controller, like this:

<h1>systems</h1>

<form id="systems">
    <fieldset>
        <div class="form-group">
            <label for="system" class="control-label">Select the system</label>
            {{#each system in controllers.systems}}
                <div class="radio">
                    <label>
                        <input type="radio" name="system" value="{{id}}" {{action 'selectSystem' id}}>
                        <span>{{name}}</span>
                    </label>
                </div>
            {{/each}}
        </div>

        {{#if systemSelected}}
            <div class="form-group">
                <label for="supplier" class="control-label">Select the supplier</label>
                {{#each suppliers}}
                    <div class="radio">
                        <label>
                            <input type="radio" name="supplier" value="{{id}}" {{action 'selectSupplier' supplier}}>
                            <span>{{name}}</span>
                        </label>
                    </div>
                {{/each}}
            </div>
        {{/if}}
    </fieldset>
</form>

There ya go. Hope that helps. There are other ways to do this, but this is the most Emberific way to do it that I know.

Upvotes: 2

Related Questions