jeudi 14 janvier 2016

How to share settings across an application?

I have a main page listing some categories / subcategories. Whenever a subcategory is clicked, the action openSubcategory is triggered:

// routes/application.js

import Ember from 'ember';

export default Ember.Route.extend({
    userSelections: Ember.inject.service('user-selections'),

    actions: {
        openSubcategory: function(categoryId, subcategoryId) {
            var userSelections = this.get('userSelections');
            userSelections.set('category', categoryId);
            userSelections.set('subcategory', subcategoryId);
            this.transitionTo('filter-categories');
        },
    }
});

To pass the selections to the corresponding controller, I am using a service:

// services/user-selections.js

import Ember from 'ember';

export default Ember.Service.extend({
    category: null,
    subcategory: null,
    init() {
        this._super(...arguments);
        this.set('category', null);
        this.set('subcategory', null);
    },
});

Which is evaluated in:

// controllers/filter-categories.js

import Ember from 'ember';

export default Ember.Controller.extend({
    userSelections: Ember.inject.service('user-selections'),
    init() {
        this._super(...arguments);
        this.get('userSelections');  // We need to get it so that we can observe it?
        // We can not declare the observers, because we need to make sure userSelections is first read
        this.addObserver('userSelections.category', function() {
            Ember.run.once(this, 'refreshProducts');
        });
        this.addObserver('userSelections.subcategory', function() {
            Ember.run.once(this, 'refreshProducts');
        });
    },
    actions: {
        changedCategory: function(selectedCategory) {
            this.set('selectedCategory', selectedCategory);
            this.get('userSelections').set('category', selectedCategory.value);
        },
        changedSubcategory: function(selectedSubcategory) {
            this.set('selectedSubcategory', selectedSubcategory);
            this.get('userSelections').set('subcategory', selectedSubcategory.value);
        },
    },
    refreshProducts: function() {
        var userSelections = this.get('userSelections'),
            category = userSelections.get('category'),
            subcategory = userSelections.get('subcategory');
        var products = this.store.filter('product', function(product) {
            var catId = parseInt(product.get('category').get('id')),
                subcatId = parseInt(product.get('subcategory').get('id'));
            if (category && catId !== category) {
                return false;
            }
            if (subcategory && subcatId !== subcategory) {
                return false;
            }
            return true;
        });
        this.set('model', products);
    },
});

Observing the userSelections (after some hacking, as seen in the comments) works: the actions are properly triggering the refreshProducts method. But it seems the method is not triggered when coming from the application route, probably because the controllers/filter-categories is not yet initialized.

(*) According to the documentation there are lots "issues" observerving services.

As a result, code needs to be written in a difficult to understand way. Is there a better pattern to share data between routes / controllers than using a service?




Aucun commentaire:

Enregistrer un commentaire