【问题标题】:Learning Ember.js- Persistence In Many To Many Relationships学习 Ember.js - 多对多关系中的持久性
【发布时间】:2013-10-29 15:41:20
【问题描述】:

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

解释应用程序

  • 用户可以添加标签
  • 用户可以添加指向所选标签的链接
  • 一个标签可以有 n 个链接
  • 一个链接可以有 n 个标签
  • 通过选择相关标签显示链接

问题
链接数据不会写入数据存储。因此,由于选择更改而对链接模型进行的本地更新将被覆盖。此外,稍后再实现真正的持久性是行不通的。

缩小问题范围

在 IndexRoute 我初始化模型:

model: function(params) {
    return {
        labels: this.get("store").find("label"),
        // TODO: this is probably wrong.
        links: Ember.A()
    };
}

一方面,我从数据存储中获取标签数据。另一方面,我用一个空的 ember 数组初始化链接数据。

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

this.get("store").find("label").filter("_")

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

// 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:http://jsbin.com/ucanam/1751/edit

如何正确存储链接数据,使更改控制器的本地数据不会破坏应用程序?

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

var link = this.get("model");
link.deleteRecord();
link.save().then(function(link) {
    var indexController = this.get('controllers.index');
    indexController.get("links").removeObject(link);
});

在您的示例中,您使用了一个承诺将对象添加到控制器。在这个代码示例中,promise 永远不会实现 - 因此它只能在没有的情况下工作。

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

我已经更新了你的 JsBin。
http://jsbin.com/ucanam/1987/edit

【问题讨论】:

    标签: javascript ember.js ember-data handlebars.js


    【解决方案1】:

    我修改了你的 JSbin http://jsbin.com/ucanam/1975/

    如果您想保留记录,您必须在存储中使用 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 https://github.com/emberjs/data/issues/772 中查看此讨论。 所以你的删除标签函数应该是(注意使用 toArray() 来制作实时数组的静态副本)

        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();
            });
        }
    

    最后注意,删除后不要忘记在记录上做一个save(),这里的jsBin:http://jsbin.com/ucanam/1989/

    【讨论】:

    • 这可能让我发疯 :-) 希望其他人在花费数小时发现您无法在 for 每个循环中删除项目之前找到这个答案。谢谢!
    【解决方案2】:

    你定义了你的模型吗?

    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>
    

    【讨论】:

    • 是的。他们在 Jsbin 中。他们有什么问题吗?
    • 我在这里做了一些修改:jsbin.com/ucanam/1754/edit 看起来您需要为链接创建一个组件,并将当前选择的标签传递给它。 For {{#each label in model}} {{#each link in label.links}} //由于你的 hasMany 关系,这将被侧载 {{link.url}} {{/each}} {{/each} }
    • 或者,您可以传入链接并使用 {{#each link in model}}{{#each label in model.labels}}{{/each}}{{/each}}。 hasMany 属性会将另一个模型作为父模型的属性进行旁加载。
    • 如果您只想让当前的实现正常工作,请为模型中的第二个对象提供链接:this.store.find('link')。
    • 不知何故 Jsbin 不适合我。我试图让我当前的实现工作。如果您将模型引用更改为 this.store.find('link') 您首先必须更改所有用途(之前是 addObject,然后是 store-push)等等。然后,无论您过滤什么,您都会获得所有链接。我用我的问题中提到的虚假过滤器解决了这个问题。但是,它仍然没有按预期工作,我无法开始解释自己的原因。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-28
    • 2017-01-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-20
    相关资源
    最近更新 更多