Ember中model.pushObject未定义及push后UI不更新问题
Ember待办列表问题解决方案
问题原因分析
model.pushObject is not a function报错:你的@model是普通JavaScript数组,pushObject是Ember内置MutableArray(如Ember.A()创建的数组)专属方法,普通数组不支持这个API,因此报错。- 用
push添加元素后UI不更新:Ember的自动追踪机制对普通数组只检测引用变化,原地修改数组(push、splice等操作)不会改变数组引用,UI无法感知内容变化,所以不会同步更新。
具体解决方法
方案一:使用Ember可追踪数组(推荐)
将数据源转换成Ember的MutableArray,这类数组的修改方法(pushObject、removeObject等)会自动触发UI更新:
- 在父组件/路由初始化数据时,用
Ember.A()包装数组:
import { A } from '@ember/array'; // 示例:路由里设置model export default class TodosRoute extends Route { model() { return A([{ id: 1, title: '初始待办' }]); } }
- 组件内的
submit方法直接使用pushObject:
@action submit(model, event) { event.preventDefault(); const lastId = model.length > 0 ? model[model.length - 1].id : 0; const newTodo = { id: lastId + 1, title: this.text }; model.pushObject(newTodo); this.text = ''; // 清空输入框 }
- 删除方法改用
removeObject:
@action delete(model, item) { model.removeObject(item); }
方案二:保持普通数组,通过替换引用触发更新
如果坚持使用普通数组,需要通过创建新数组替换原引用来触发Ember的追踪更新,同时遵循Ember数据流向(子组件不直接修改父组件数据,通过action通知父组件更新):
- 父组件定义数据和更新action:
import { tracked } from '@glimmer/tracking'; import { action } from '@ember/object'; export default class ParentComponent extends Component { @tracked todos = [{ id: 1, title: '初始待办' }]; @action addTodo(newTodo) { // 创建新数组替换原引用,触发UI更新 this.todos = [...this.todos, newTodo]; } @action removeTodo(item) { this.todos = this.todos.filter(todo => todo.id !== item.id); } }
- 子组件模板传递action,不再直接传递model:
<ul class="hand"> {{#each @todos as |item|}} <li>{{item.title}}</li> <button onclick={{fn this.removeTodo item}} type='button'>delete</button> {{/each}} </ul> <form onsubmit={{fn this.submit}}> <Input placeholder='Add todo' type='text' value={{this.text}} onchange={{fn this.onChange}} /> <button type='submit'>Add</button> </form>
- 子组件内调用父组件的action:
@tracked text = ""; @action submit(event) { event.preventDefault(); const lastId = this.args.todos.length > 0 ? this.args.todos[this.args.todos.length - 1].id : 0; const newTodo = { id: lastId + 1, title: this.text }; this.args.addTodo(newTodo); this.text = ''; } @action onChange(e) { this.text = e.target.value; } @action removeTodo(item) { this.args.removeTodo(item); }
额外注意点
- 不要直接修改子组件接收的
@model(外部传递的属性),遵循「数据向下传,动作向上发」的单向数据流原则,避免数据状态混乱。 - 普通对象不需要用Ember的
set方法修改属性,直接赋值即可(更推荐用filter创建新数组的方式删除元素)。
内容的提问来源于stack exchange,提问作者star lord
相关产品推荐
相关产品推荐

