问题
I'm trying to have a route's model hook return some array that is constantly updated via a polling mechanism using Ember later. The route file looks like this:
export default class IndexRoute extends Route {
recent: [],
init() {
...
this.getRecent();
}
getRecent() {
// poll data / fetch latest
this.recent.push(newStuff);
later(this, this.getRecent, 2000);
}
model() {
return this.recent;
}
}
Then in my controller, I wanted to create a @computed
/ @tracked
property based on the route's model
:
export default class IndexController extends Controller {
// @tracked model; // this also didn't work
@computed('model.@each') // this doesn't work
get computedModel() {
console.log('computedModel'); // prints only once, when the model hook is first run
return this.model;
}
}
I thought what this SO post suggested would have worked but it didn't :(
I saw this post but this was for Ember 1.13 so not exactly a modern solution.
Similarly this post had outdated content too.
Is what I'm trying to do possible? Alternatively I was thinking of moving the data into the Controller and making a computed property of a Controller variable instead. Taking all suggestions!
回答1:
The fundamental problem with your current approach is that you are not using Ember Array specific functions. There is often some magic that happens when you create arrays in Ember that automatically creates them as Ember arrays (at least this was the case when using the old style syntax with .create
and when prototype extensions were allowed). You can always explicitly create an Ember array with:
import { A } from '@ember/array';
export default class ApplicationRoute extends Route{
recent = A([]);
}
Anyway, when you just use this.recent.push(newStuff);
, this native array prototype is not instrumented in a way that Ember's tracking/observer system can know that the a new value has been added to this.recent
and subsequently trigger a rerender. This applies to both tracked properties as well as the traditional observer system pre-Octane.
Instead, when interacting with arrays that are being displayed in templates (ie arrays that need to be observed), you must use special Ember.Array
specific functions pushObject
like this.recent.pushObject(newStuff)
.
getRecent() {
// poll data / fetch latest
this.recent.pushObject(this.current);
this.current = this.current + 1;
later(this, this.getRecent, 2000);
}
If you wanted to go full on tracked property style, you can avoid using the Ember array, but you must force a recompute by reassigning the array to the tracked property. Here's an example in a component
import Component from '@glimmer/component';
import { tracked} from '@glimmer/tracking';
import { later } from '@ember/runloop';
export default class extends Component {
@tracked
trackedArray = [];
current = 0;
constructor(){
super(...arguments);
this.doPoll();
}
doPoll() {
// essentially pushing but via a reassign with the new element
// you could also `pushObject` here if that feels better
this.trackedArray = [...this.trackedArray, this.current];
this.current = this.current + 1;
later(this, this.doPoll, 2000);
}
}
Here is a Ember Twiddle that shows both approaches in action.
PS. The model
property of a route is only quasi-dynamic. During the transition, the return value of the model
hook is automatically assigned to the route's currentModel
property. This value is then passed into setupController
as the second parameter and automatically assigned to the controller's model
property if no setupController
is defined or if the setupController
invokes super
of the base implementation. When modelFor
is called for a particular route, the currentModel
property is returned and model
is not reinvoked. This is all to say that having the poll in the model
function itself would not automatically update the controller's model
property. Everything works fine in your example since the model
reference never changes (you're just mutating the array).
来源:https://stackoverflow.com/questions/63169006/is-it-possible-to-make-a-routes-model-a-computed-tracked-property