I'm trying out Octane, and for some reason, if I show an array in a template and I add a new object to it, the UI doesn't update. What am I doing wrong?
Here is my template:
<label for="new-field-name">Field Name</label>
<Input id="new-field-name" @value={{this.newFieldName}} type="text" />
<button {{on "click" this.addField}}>Add field</button>
{{#each this.fields as |field|}}
<p>{{field.name}}</p>
{{/each}}
And the component:
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';
export default class ConfigControlsComponent extends Component {
@tracked fields = []
@tracked newFieldName = ''
@action addField() {
this.fields.push({
name: this.newFieldName
})
console.log(this.fields)
}
}
The console.log
shows the array with the new object added to it, and the fields
array is tracked, but nothing changes when I click the button.
Tracked properties are imported from @glimmer/tracking and used as a decorator to mark the tracked properties. If the property is marked for tracking, the DOM will be re-rendered each time the value of the tracked property changes. Hence, tracked properties are a fundamental change in how Ember handles the state.
Tracked properties replace computed properties. Unlike computed properties, which require you to annotate every getter with the values it depends on, tracked properties only require you to annotate the values that are trackable, that is values that: Change over the lifetime of their owner (such as a component) and.
Which release of Ember is Octane? The Octane Edition was declared for Ember 3.15.
When you use tracked
with arrays, you need to "reset" the array so that Ember notices that there has been a change. Try doing this.fields = this.fields
after pushing a new object into the array.
Edit: some linters will guard against self-assignment. So, instead, we could pull from immutability patterns, and set using a new array, as shown below.
export default class ConfigControlsComponent extends Component {
@tracked fields = []
@tracked newFieldName = ''
@action addField() {
// add this line
this.fields = [...this.fields, {
name: this.newFieldName
}];
}
}
If you are trying to use tracked
with an object instead of an array, you have two options:
First, you could create a class where all the properties on the object are tracked:
import { tracked } from '@glimmer/tracking';
class Address {
@tracked street;
@tracked city;
}
class Person {
address = new Address();
get fullAddress() {
let { street, city } = this.address;
return `${street}, ${city}`;
}
}
Or, second, you could use the same "reset" approach as the array example above.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With