jeudi 4 août 2016

In Ember.js, is it possible to force computed properties defined on a route to update when the model refreshes?

In Ember.js, is it possible to force computed properties defined on a route to update when the model refreshes?

I am noticing that the first time I hit a route, I will see the functions that back computed properties invoked, but if the route's model is refreshed because a query parameter (e.g. page) changes, the functions backing the computed properties are not invoked again. I assume that this is because Ember doesn't believe the computed properties to have changed, in part because it doesn't believe the properties they are dependent on to have changed. For example:

// routes/example.js
export default Ember.Route.extend({

  limit: 10,
  queryParams: {
    page: {
      refreshModel: true
    }
  },

  offset: computed("limit", function() {
    console.log("IN: offset");
    let params = this.paramsFor(this.get("routeName"));
    if (params.page) {
      // calculate offset using the "params.page" value
    }
    return 0;
  }),

  paginator: computed("limit", "offset", "query", function() {
    console.log("IN: paginator");
    // Calls: this.get("offset") and this.get("query") as part of its invocation
    // Returns an object that can resolve the pagination of the model.
    ...
  }),

  query: computed(function() {
    console.log("IN: query");
    ...
  }),

  model(params) {
    console.log("IN: model");
    let paginator = this.get("paginator");
    ...
  },

  setupController(controller, model) {
    this._super(controller, model);
    controller.setProperties({
      offset: this.get("offset"),
      limit: this.get("limit"),
      pageCount: model.meta.total,
    });
  }
});

... and the controller:

// controllers/example.js
export default Ember.Controller.extend({

  actions: {

    firstPage: function() {
      this.transitionToRoute({
        queryParams: {
          page: 1
        }
      });
    },

    lastPage: function() {
      let lastPage = Math.ceil(this.get('pageCount')/this.get('limit'));
      this.transitionToRoute({
        queryParams: {
          page: lastPage
        }
      });
    },

    nextPage: function() {
      let totalPages = Math.ceil(this.get('pageCount')/this.get('limit'));
      if(this.incrementProperty('page') > totalPages) {
         this.set('page', totalPages);
      }

      this.transitionToRoute({
        queryParams: {
          page: this.get('page')
        }
      });
    },

    previousPage: function() {
      if(this.decrementProperty('page') < 1) {
        this.set('page', 1);
      }

      this.transitionToRoute({
        queryParams: {
          page: this.get('page')
        }
      });
    }
  },

  ...

  /**
   * The result page that a user is currently viewing.
   */
  page: 1,

  /**
   * The query parameters.
   */
  queryParams: ["page"],

});

The first time I load the route by hitting the associated URL in the browser I see:

IN: model
IN: paginator
IN: offset
IN: query

If I click on a button in the rendered template which changes the ?page query param, such as with the nextPage action, I see that it calls in to refresh the model but does not invoke any of the logic behind the computed properties, e.g.:

IN: model

I've tried adding controller.page to the list of dependent properties, but this does not seem to change the observed behavior. As an aside, functions appear to always be called (as I would expect).

Should I not be using computed properties in the route? It seems like if I want to use computed properties I need to somehow notify Ember to re-process them when the model is refreshed whether Ember believes them to have changed or not.




Aucun commentaire:

Enregistrer un commentaire