Javascript 学习Ember.js-多对多关系中的持久性

Javascript 学习Ember.js-多对多关系中的持久性,javascript,ember.js,ember-data,handlebars.js,Javascript,Ember.js,Ember Data,Handlebars.js,为了学习Ember.js,我开始编写一个小型书签应用程序。我现在正在努力解决与错误数据处理相关的问题 解释应用程序 用户可以添加标签 用户可以添加指向选定标签的链接 一个标签可以有n个链接 链接可以有n个标签 通过选择关联的标签显示链接 问题 链接数据未写入数据存储。因此,将覆盖由于选择更改而对链接模型的本地更新。而且,以后实现真正的持久性也不起作用 缩小问题范围 在IndexRoute中,我初始化模型: model: function(params) { return {

为了学习Ember.js,我开始编写一个小型书签应用程序。我现在正在努力解决与错误数据处理相关的问题

解释应用程序

  • 用户可以添加标签
  • 用户可以添加指向选定标签的链接
  • 一个标签可以有n个链接
  • 链接可以有n个标签
  • 通过选择关联的标签显示链接
问题
链接数据未写入数据存储。因此,将覆盖由于选择更改而对链接模型的本地更新。而且,以后实现真正的持久性也不起作用

缩小问题范围

在IndexRoute中,我初始化模型:

model: function(params) {
    return {
        labels: this.get("store").find("label"),
        // TODO: this is probably wrong.
        links: Ember.A()
    };
}
在一侧,我从数据存储中获取标签数据。另一方面,我用一个空的余烬数组初始化链接数据

我认为这是故障的根源,但我不知道如何正确实施。 我试图将其替换为对存储适配器的虚假引用:

this.get(“store”).find(“label”).filter(“\u”)

这既不对,也不能正常工作。然后继续到无法使用存储适配器更新记录的地步:

// TODO: this is probably wrong.
this.get("links").addObject({
    name: linkName,
    url: linkUrl,
    labels: this.selectedLabels
});
/*store.push("link", {
    name: newLink,
    url: linkUrl,
    labels: this.selectedLabels
});*/
等等

Jsbin:

如何正确存储链接数据,以便更改控制器的本地数据不会中断应用程序

编辑:
我想我在你的建议中发现了概念上的错误。这是否意味着我也必须处理本地副本

var link = this.get("model");
link.deleteRecord();
link.save().then(function(link) {
    var indexController = this.get('controllers.index');
    indexController.get("links").removeObject(link);
});
在您的示例中,您使用承诺将对象添加到控制器。在这个代码示例中,承诺永远不会实现-因此它只在没有承诺的情况下工作

同样在
LabelController
中,remove方法也应该删除相关链接。如果我在forEach循环中使用
deleteRecord
,它只删除一个标签,然后以某种方式使循环退出。这是故意的还是我犯了错误

我已更新您的JsBin。

您定义了您的模型吗

App.Label = DS.Model.extend({
    title: DS.attr('string'),
    links: DS.hasMany('link')
});

App.Link = DS.Model.extend({
    name: DS.attr('string'),
    url: DS.attr('string'),
    labels: DS.hasMany('label')
});

App.IndexRoute = Ember.Route.extend({
    model: function() {
        this.store.find('label')
    }
});

App.LabelController = Ember.ObjectController.extend({
    actions:
        <!-- functions here -->
});

<script type='text/handlebars' data-template-name="index">
    {{#each label in model itemController='label'}}
        {{label.title}}
            {{#each link in label.links}}
                {{link.title}}
            {{/each}}
    {{/each}}
</script>
App.Label=DS.Model.extend({
标题:DS.attr('string'),
links:DS.hasMany('link')
});
App.Link=DS.Model.extend({
名称:DS.attr('string'),
url:DS.attr('string'),
标签:DS.hasMany('label'))
});
App.IndexRoute=Ember.Route.extend({
模型:函数(){
this.store.find('label'))
}
});
App.LabelController=Ember.ObjectController.extend({
行动:
});
{{{#模型itemController='label'}中的每个标签
{{label.title}
{{{#label.links中的每个链接}
{{link.title}
{{/每个}}
{{/每个}}
我修改了你的JSbin

如果要保留记录,必须使用createRecord()在存储中创建记录,然后使用save()保存记录。新的newLink函数

    newLink: function() {
        var store = this.get("store"),
            linkName = this.get("linkName"),
            linkUrl = this.get("linkUrl"),
            links = this.get("links"),
            selectedLabels = this.get('selectedLabels');

        if(selectedLabels.get('length') > 0) {
            if(linkName.length > 0 && linkUrl.length > 0) {
              var newLink = store.createRecord('link',
                {
                    name: linkName,
                    url: linkUrl
                });
              selectedLabels.forEach(function(label){
                newLink.get('labels').addObject(label);
              });
              newLink.save().then(function(link){
                links.addObject(link);
              });
              this.set("linkName", "");
              this.set("linkUrl", "");
            }
        } else {
            alert("You have to select a label!");
        }
    },
对于删除记录来说,使用forEach存在问题,因为对存储的查找结果是一个活动数组。您可以在GitHub中看到此讨论。 因此,您的remove label函数应该是(注意使用toArray()来创建活动阵列的静态副本)


最后请注意,删除记录后,不要忘记对记录进行保存(),jsBin此处:

是。它们在Jsbin中。它们有什么问题吗?我在这里做了一些修改:看起来您需要为链接创建一个组件,并将其传递给当前选择的标签。对于{{{{35;模型中的每个标签}{{{{{35; label.links}}中的每个链接}//,由于您有许多关系{{{link.url}{{{{/each}}{/each}}{/each}},因此这将被侧载,或者,您可以传入链接并使用{{{{{{model.labels}中的每个链接}{{{{/each}}。hasMany属性将另一个模型作为父对象的属性进行侧加载。如果您只想让当前实现正常工作,请为模型中的第二个对象创建链接:this.store.find('link')。不知何故,Jsbin对我不起作用。我试图让我当前的实现正常工作。如果将模型引用更改为this.store.find('link'),则首先必须更改所有用途(以前是addObject,然后是store push),依此类推。然后你得到所有链接,不管你过滤什么。我用问题中提到的假过滤器解决了这个问题。然而,它仍然没有像它应该的那样工作,我也无法开始解释自己为什么。这可能会让我发疯:-)希望其他人在花几个小时来弄明白你不能删除for-each循环中的项目之前找到这个答案。谢谢
    remove: function() {
        var indexController = this.get('controllers.index'),
            label = this.get('model'),
            linksPromise = this.get('store').find('link');

        linksPromise.then(function(links){
            links.toArray().forEach(function(link){
                var linkLabels = link.get('labels'),
                    linkLabelsIds = linkLabels.mapProperty('id');

                if(linkLabelsIds.contains(label.get("id"))) {
                    if(linkLabelsIds.get("length") == 1) {
                        console.log("delete link: "+link.get("name"));
                        indexController.get("links").removeObject(link);
                        link.deleteRecord();
                        link.save();
                    }
                }
            });

            label.deleteRecord();
            label.save();
        });
    }