Search code examples
ember.jsember-octane

How to pass a @tracked object from an Ember route model hook


My question is two-fold:

  1. Where is the best place to put some kind of polling logic - in the route file right?

  2. How do I pass this constantly updating value from the Route to some child component? Labeling some variable as "@tracked" and then passing the tracked variable via the model hook?

Let's say I have something like this:

routes/index.js

export default class IndexRoute extends Route {
  @tracked
  recent: {
    A: 0,
    ...
  },

  constructor() {
    super(...arguments);
    this.getRecent();
  }

  getRecent() {
    // poll data / fetch latest
    const {A, ...partialObject} = this.recent;
    
    this.recent = { ...partialObject, A: <some new value fetched>};;
    
    later(this, this.getRecent, 2000);
  }

  model() {
    return this.recent;
  }
}

application.hbs

<p>constantly updating "this.recent": {{ this.model.A }} </p>

I thought if I use the model hook like this, it would be tracked and therefore auto-update but that was not the case. I have this sample Ember Twiddle that emulates what I'm trying to do. I tried to force a re-compute by reassigning the entire variable but it didn't work.

This question is a deeper dive from my initial question here.


Solution

  • You are returning a reference to object stored in this.recent in your model hook. But the getRecent method does not change that object but overrides this.recent. After the first execution of getRecent method the model of the route and this.recent aren't the same object anymore. The model of the route, which you can access through this.modelFor(this.routeName) is the initial value and this.recent is the new value.

    You want to mutate the object returned from model hook instead.

    The object given in your example has a fixed schema. This allows you to mark the property A as tracked:

      recent: {
        @tracked A: 0,
        ...
      }
    

    As currently you return the value of this.recent in your model hook. But instead of overwriting it in getRecent method, you are only changing the value of it's property A:

      getRecent() {  
        this.recent.A = <some new value fetched>;
        
        later(this, this.getRecent, 2000);
      }
    

    If you don't know the schema of the object returned in model hook or if you are dealing with an array, it's a little bit more complicated. You wouldn't have a property to decorate with @tracked. I would recommend to use the tracked-built-ins package in that case.

    For arrays you can also fallback to legacy MutableArray from @ember/array/mutable package. But you must make sure in that case that you use it's custom methods to manipulate the array (e.g. pushObject instead of push).