Ember多对多关系 - 添加一个新对象(如果尚不存在)

时间:2015-02-16 04:49:26

标签: javascript ember.js ember-data

我有两个模型彼此有多对多的关系 - 第一个是' recipe',第二个是'标签':

App.ApplicationAdapter = DS.FixtureAdapter.extend({});

App.Recipe = DS.Model.extend({
    title: attr('string'),
    source: attr('string'),
    notes: attr('string'),
    isFavourite: attr('boolean', {defaultValue: false}),
    tags: hasMany('tag', { async: true })
});

App.Tag = DS.Model.extend({
    name: attr('string'),
    recipes: hasMany('recipe', { async: true })
});

在我的模板中,我有一个用于向配方添加标签的输入。 用户可以通过用逗号分隔每个标记来添加一个标记或多个标记。

{{input type="text" enter="AddTags" placeholder="add a tag (or two!)"}}

这称为“添加标签”'在我的RecipeController中:

App.RecipeController = Ember.ObjectController.extend({
    actions: {
        AddTags: function(tags){
            var store = this.store;
            var thisRecipe = this.get('model');
            var thisRecipeTags = thisRecipe.get('tags');
            var addedTags = tags.split(',');

            for (i = 0; i < addedTags.length; i++) {
                tag = store.createRecord('tag', {
                    name: addedTags[i]
                });

                thisRecipeTags.addObject(tag);
            }
        }
    }
});

这非常有效 - 然而 - 我想重构该函数,以便只创建一个新标记如果已经具有相同名称的存在:

AddTags: function(tags){
    var store = this.store;
    var thisRecipe = this.get('model');
    var thisRecipeTags = thisRecipe.get('tags');
    var addedTags = tags.split(',');
    for (i = 0; i < addedTags.length; i++) {
        // If exisiting tag where name == addedTags[i]
            tag = existing tag object here
        // Else create a new tag
            tag = store.createRecord('tag', {
                name: addedTags[i]
            });

        thisRecipeTags.addObject(tag);
    }
}

我该怎么做?

更新:我在这里设置了一个带有相关代码的JS bin:http://jsbin.com/kixexe/1/edit?js,output

2 个答案:

答案 0 :(得分:2)

工作演示here

现在有一些解释......

您在jsbin中使用DS.FixtureAdapter,如果您要查询灯具(请参阅here),则要求您实施queryFixtures方法,就像您在按名称查询时所做的那样。快速实现这一点可以如下:

MyApp.TagAdapter = DS.FixtureAdapter.extend({
    queryFixtures: function(fixtures, query){
      var result = [];
      var tag = fixtures.findBy('name', query.name);
      if(tag){
        result.push(tag);
      }

    return result;
  }
});

当您查询商店时,您会收到使用then()

可以进入的承诺

如果你没有找到任何东西 - 你可以继续前所未有的......

如果 找到现有标记 - 您可以将其添加到其他标记,然后保存标记和配方记录,因为这是多对多关系。

<强>更新

更新了解决方案here

你使用for循环时会感到困惑,尤其是在使用promises时,这通常不是你想要的。相反,Ember有一个更典型/功能性的forEach()方法我也修剪了标签末端的空白,因为这可能是你想要的map()。请参阅here,详细了解map()forEach()

// trim whitespace
var addedTags = tags.split(',').map(function(str){ 
   return  str.replace(/^\s+|\s+$/gm,'');
});

// loop through tags
addedTags.forEach(function(tagName){
  store.find('tag', { name: tagName }).then(function(results){
    if(results.content.length){
      thisRecipeTags.addObject(results.content[0]);  
      thisRecipeTags.save();
      thisRecipe.save();
    } 
    else {
      var tag = store.createRecord('tag', {
        name: tagName
      });
      thisRecipeTags.addObject(tag);
      thisRecipeTags.save();
      thisRecipe.save();                    
    }
  });
});

更新#2

标签未正确删除。因为这是一个双向映射,显然你需要从配方中删除标签(你正在做的)和标签中的ALSO配方,你没有这样做。更新后的代码如下所示:

removeTag: function(tag) {
  var recipe = this.get('model');
  recipe.get('tags').removeObject(tag);
  tag.get('recipes').removeObject(recipe.get('id'));
  tag.save();
  recipe.save();
},

工作(希望这次真实;)解决方案here

答案 1 :(得分:1)

您可以按如下方式搜索现有标签:

var existing = store.find('tag', { name: addedTags[i] });

所以,你可以有以下逻辑:

AddTags: function(tags){
    var store = this.store;
    var thisRecipe = this.get('model');
    var thisRecipeTags = thisRecipe.get('tags');
    var addedTags = tags.split(',');
    for (i = 0; i < addedTags.length; i++) {
        var existing = store.find('tag', { name: addedTags[i] });
        if(existing){
            tag = existing        
        } else {
            tag = store.createRecord('tag', {
                name: addedTags[i]
            });        
        }

        thisRecipeTags.addObject(tag);
    }
}

我目前无法对此进行测试,因此可能需要进行一些更改才能解释承诺等等 - 但这应该会让您或多或少地得到所需的信息。