hasMany / belongsTo:如何更改项目的所有权(从一个父项迁移到另一个项目)?

时间:2014-06-02 06:21:12

标签: ember.js

我有一个与自己相关的hasManybelongsTo模型

Todos.Todo = DS.Model.extend({
  title: DS.attr('string'),
  isCompleted: DS.attr('boolean'),
  parent: DS.belongsTo('todo', {inverse: 'children'}),
  children: DS.hasMany('todo', {inverse: 'parent'})
});

我将让用户相互拖放todos让他重新排列层次结构。但对于一个不熟悉Ember的人来说,这是一项艰巨的任务,所以我决定从更简单的事情开始:

每个待办事项都包含可能的父母的下拉列表。用户可以从列表中选择任何父级,并使用该父级更新待办事项。

下拉列表中还有一个“无父”项。当为待办事项选择它时,此待办事项将更新为不包含父项。

我执行修改todo父级的方式非常简单:

  1. 从Todos ArrayController(controllers.todos.model)中获取一组模型。
  2. 过滤集合,使其仅包含id等于请求ID的记录(来自用户在下拉列表中的选择)。
  3. 从过滤后的集合中抓取第一个也是唯一一个记录。
  4. 将当前记录的parent字段设置为抓取的记录对象。
  5. 保存好的记录。
  6. 我还在todo控制器上创建了几个简单的属性,以查看每个待办事项是否有父母和孩子:

      hasChildren: (function() {
        return this.get('model.children').get('length') > 0;
      }).property('model.parent', 'model.children'),
    
      hasParent: (function() {
        return this.get('model.parent') !== null;
      }).property('model.parent', 'model.children'),
    

    hasParent属性我可以看出,当我使用下拉列表更改todo的父级时,父级会被修改,是的!修改通过页面刷新(我正在使用本地存储适配器)持续存在,所以我假设我正确地执行更新todo的父级。

    问题是当我更新todo A上的'parent'属性以包含待办事项B时,todo B上的hasChildren属性没有被更新。这使我无法自动刷新页面上的待办事项层次结构。

    我曾经认为,当我更新孩子的children属性时,Ember Data会自动更新父级的parent属性。那就是inverse的东西,对吗?如果Ember Data不能自动维护关系的完整性,为什么要知道关系的相反属性?

    所以要么我错了,我应该在更新子todo上的children属性时手动更新旧父母和新父母的parent属性(冒着破坏关系完整性的危险) ,或者我正以错误的方式改变父母。

    问题是:在hasMany / belongsTo关系中更改项目所有权的正确方法是什么?

    小提琴:http://jsbin.com/UDoPajA/220/edit

    ppcano的UPD1:

    我已经尝试过你的方法并没有任何区别。 :(我已经应用了您的建议,并且模型hasParent属性确实会动态更新。但haschildren属性没有。

    但后来我尝试设置hasChildren属性来观察todos.@each.parent并且它开始更新了,是的!

    我仍然不明白:为了改变记录的关系,我必须做很多工作。这就是我如何应用你的建议:

    var model = this.get('model');
    
    // Removing current todo from the list of children of the former parent
    model
      .get('parent')
      .get('children')
      .removeObject(model);
    
    // Adding current todo to the list of children of the new parent
    this
      .get('todos')
      .filter( function(candidateTodo) {
        return candidateTodo.get('id') === newParentId;
      })[0]
      .pushObject(model);
    
    model.save();    
    

    这是一项很多工作,也更容易打破关系的完整性。为什么我不能这样做呢?

    var model = this.get('model');
    var newParent = this
      .get('todos')
      .filter( function(candidateTodo) {
        return candidateTodo.get('id') === newParentId;
      })[0];
    
    model.set('parent', newParent);
    model.save();
    

    我已经尝试过了,但它不起作用。 :(父属性更新,但子属性不会。

    有没有办法从belognsTo端正确更新关系,而不是做额外的工作来更新hasMany端的关系。

    我发现this answer但它基本上意味着两端的手动更新,我也不喜欢它。提问者也惊讶于Ember需要手动完成。

    有一个更奇怪的事情。为了更新当前模型的parent属性,我们必须修改另一端的关系(在父项的children属性上调用方法)。如果我们在父项上执行此操作,为什么需要在当前模型上执行.save()而不是父模型才能保持更改?为什么hasChildren属性必须遵守todos.@each.parent并且观察model.children不起作用?

    还有另一个未解决的问题:当添加新的待办事项或删除现有的待办事项时,所有待办事项的“父:”下拉列表将重置为“无父”状态!但是,如果我刷新页面,则会恢复下拉列表中正确的父选择。 Whaaat?

    请帮我解决这些问题。小提琴:http://jsbin.com/UDoPajA/231/edit

1 个答案:

答案 0 :(得分:2)

如果设置inverse属性,则只需要从关系中推送/删除对象,而ember-data将注意更新依赖关系。

parent.get('children').then(function(children){
  children.removeObject(child);
})

parent.get('children').then(function(children){
  children.pushObject(child);
})

由于ember-data的当前状态(使用v1.0.0-beta.8),您可以将hasParenthasChildren定义为:

  hasChildren: Ember.computed.notEmpty('children.[]'),
  hasParent: Ember.computed.notEmpty('parent.content')

请看这个例子:http://emberjs.jsbin.com/pubij/2/edit