【问题标题】:hasMany/belongsTo: how do i change the ownership of an item (move from one parent to another)?hasMany/belongsTo:如何更改项目的所有权(从一个父级移动到另一个父级)?
【发布时间】:2014-06-02 06:21:12
【问题描述】:

我有一个模型,它与自身相关的 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'})
});

我打算让用户在彼此上拖放待办事项,让他重新排列层次结构。但这对于不熟悉 Ember 的人来说是一项艰巨的任务,所以我决定从更简单的开始:

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

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

我对 todo 的父项进行修改的方式非常简单:

  1. 从 Todos ArrayController (controllers.todos.model) 获取模型集合。
  2. 过滤集合,使其仅包含 id 等于请求 id 的记录(来自用户在下拉列表中的选择)。
  3. 从过滤后的集合中获取第一条也是唯一一条记录。
  4. 将当前记录的parent字段`设置为抓取的记录对象。
  5. 保存当前记录。

我还在 todo 控制器上创建了几个简单的属性来查看每个 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 的父项。

问题是当我更新待办事项 A 上的“父”属性以包含待办事项 B 时,待办事项 B 上的 hasChildren 属性未更新。这使我无法自动刷新页面上待办事项的层次结构。

我曾经认为当我更新子级的parent 属性时,Ember Data 应该自动更新父级的children 属性。这就是inverse 的用途,对吧?如果 Ember Data 不应该自动维护关系的完整性,它为什么要知道关系的相反属性?

所以要么我错了,当我更新子待办事项的parent 属性时,我应该手动更新新老父母的children 属性(有可能破坏关系的完整性),或者我' m 以错误的方式更改父级。

问题是:在 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不起作用?

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

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

【问题讨论】:

    标签: ember.js


    【解决方案1】:

    如果您设置了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

    【讨论】:

    • 感谢您的回答,@ppcano。它有所帮助,但我仍然在努力解决问题。请查看我更新的问题并尽可能详细地回复。谢谢。
    • 我更新了我的响应和示例以正确创建 hasParent 和 hasChildren CP。考虑到我在关系中使用的是 async:true,这将是最终版本中的默认方式,因此,您必须以 Promise 的形式访问:this.get('parent').then。我建议您将您的疑虑拆分为多个 SO 孤立的问题,很难遵循其他开发人员的实现并尝试同时解决不同的问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-07-06
    • 1970-01-01
    • 2021-12-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多