【问题标题】:Backbone model fetch() not calling parse骨干模型 fetch() 不调用 parse
【发布时间】:2017-11-09 21:44:47
【问题描述】:

我有两个在 Backbone 模型上调用 fetch 的函数。第一个使用 id 创建模型的新实例并调用 fetch(),第二个使用 id 从集合中检索现有模型实例并调用 fetch()。第一个触发了模型的解析函数,第二个没有触发……不知道为什么。

第一个(触发器解析)

App.fetchItemById = function (id) {
    App.myItem = new App.Item({id: id});
    App.myItem.fetch({
        traditional: true,
        data: {
            elements: 'all',
            format:'json',
        },
        success: function(){
            App.myItemView = new App.ItemView({
                el: $('#active-item'),
                model: App.myItem,
            });
            App.myItemView.render();
        }
    });
};

第二个(不触发解析)

App.fetchItemFromCollectionById = function (id) {
    App.myItem = App.myItemCollection.get(id);
    App.myItem.fetch({
        traditional: true,
        data: {
            elements: 'all',
            format:'json',
        },
        success: function(){
            App.myItemView = new App.ItemView({
                el: $('#active-item'),
                model: App.myItem,
            });
            App.myItemView.render();
        }
    });
};

我读过的所有文档都说模型的解析函数总是在 fetch 时调用。

有人知道为什么第二个没有触发解析吗?

这是模型定义:

App.Item = Backbone.Model.extend({
    urlRoot: '/api/v1/item/',
    defaults: {

    },
    initialize: function(){

    },
    parse : function(response){
        console.log('parsing');
        if (response.stat) {
            if (response.content.subitems) {
                this.set(‘subitems’, new App.SubitemList(response.content.subitems, {parse:true}));
                delete response.content.subitems;
                this
            }

            return response.content;
        } else {
            return response;
        }
    },
});

已修复,感谢 EMILE 和 COREY - 以下解决方案

原来,当我第一次加载 App.MyItemCollection 时,集合中的模型只是通用模型,没有正确地转换为 App.Item 的实例。将“模型:App.Item”添加到集合定义中解决了这个问题。见下文:

原创

App.ItemList = Backbone.Collection.extend({
    url: '/api/v1/item/',
    parse : function(response){
        if (response.stat) {
            return _.map(response.content, function(model, id) {
                model.id = id;
                return model;
            });
        }
    }
});

更新,解决问题

App.ItemList = Backbone.Collection.extend({
    url: '/api/v1/item/',
    model: App.Item,
    parse : function(response){
        if (response.stat) {
            return _.map(response.content, function(model, id) {
                model.id = id;
                return model;
            });
        }
    }
});

【问题讨论】:

  • 已确认。该模型在获取前和获取后都有一个 id 属性,并触发了 fetch:success 回调。
  • Backbone 的哪个版本? 1.2.0
  • 我更新到 1.3.3 也遇到了同样的问题。
  • myItemCollection 是什么样的? model 属性是否设置为使用 App.Item 作为其模型?
  • @CoryDanielson,就是这样!当我第一次加载集合时,我没有正确地将模型转换为 App.Item 的实例。当我添加它时,它起作用了。谢谢你。我会用工作代码更新我的问题。

标签: javascript backbone.js


【解决方案1】:

parse 仅在成功时调用 fetch

Backbone source中,我们可以看到parse函数只有在fetch异步请求成功时才会被调用。

fetch: function(options) {
  options = _.extend({parse: true}, options);
  var model = this;
  var success = options.success;

  // The success callback is replaced with this one.
  options.success = function(resp) {
    // parse is called only if the fetch succeeded
    var serverAttrs = options.parse ? model.parse(resp, options) : resp;
    // ...snip...
    // then the provided callback, if any, is called
    if (success) success.call(options.context, model, resp, options);

  };
  wrapError(this, options);
  return this.sync('read', this, options);
},

documentation on parse 说:

parse 在服务器返回模型数据时调用,在 fetchsave

假设fetch失败,服务器没有返回模型数据,所以不会调用parse。

要使fetch 成功使用现有模型,默认行为要求模型在其attributes 哈希中具有id

cid 仅用于本地区分模型,不能单独用于获取。

模型的特殊属性,cidclient id 是唯一的 标识符在第一个模型时自动分配给所有模型 创建。当模型尚未保存到时,客户端 ID 很方便 服务器,并且还没有最终的真实 id,但已经 需要在 UI 中可见。


确保集合使用我们自己的模型

作为mentioned by Cory Danielson,如果myItemCollection集合上没有设置model property,将调用默认模型parse,而不是你的App.Item模型的parse

不应在 parse 函数中实例化模型,因为这是集合的责任。

App.ItemList = Backbone.Collection.extend({
    model: App.Item, // make sure to set this property
    url: '/api/v1/item/',
    parse : function(response){
        if (response.stat) {
            return _.map(response.content, function(model, id) {
                model.id = id;
                return model;
            });
        }
    }
});

如果我们没有在集合上设置model property,任何时候我们将在集合上使用addset,我们将面临同样的问题,我们的新模型将是Backbone.Model实例而不是 App.Item 实例。


避免parse 中的副作用

你的 parse 函数有一些你应该知道的代码味道:

  • parse 不应该对模型有任何副作用。它应该接收数据并返回转换后的数据,就是这样。
  • 它应该总是返回数据。在你的情况下,它只返回一个条件下的数据,否则返回undefined
  • 使用 delete 效率低下,最好只返回一个新对象。无论如何,这是您应该避免的另一个副作用。

小心嵌套模型和集合

话虽如此,我看到您在解析过程中将集合嵌套在模型的 attributes 哈希中。出于以下几个原因,我建议不要使用这种模式:

  • 模型变得非常脆弱,您需要非常小心地始终调用parse: true,并且永远不要在模型之外设置subitems
  • 一旦创建模型,它就会为子项创建所有模型,这对于大量模型而言效率低下。 延迟初始化子项会有所帮助。
  • 现在使用 default 更加复杂,因为您必须记住属性中有一个集合。
  • 保存模型更复杂出于同样的原因。
  • 子项中的更改不会触发父模型中的事件

我已经在这里回答了这些问题的多种解决方案:

【讨论】:

  • 模型在获取前后都有一个id属性,并触发了fetch:success回调。我只使用 cid 从集合中获取对象。
  • 从使用 cid 更改为 id,结果相同。
  • 谢谢埃米尔,很棒的信息。问题是我在加载我的收藏时没有将模型转换为 App.Item 的实例。我会在一分钟内发布工作解决方案。
【解决方案2】:

您的集合可能没有为其模型属性使用相同的模型类。这就是为什么不会调用对该特定模型的 parse 的原因。

如果model 属性未设置为您期望的模型,则不会在该模型上调用 parse。默认情况下,该集合将使用 Backbone.Model。

MyItemCollection = Backbone.Collection.extend({
    model: App.Item
});

你可能有这样的东西

MyItemCollection = Backbone.Collection.extend({
    model: Backbone.Model.extend({
        urlRoot: '/api/v1/item/'
    })
});

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-03-15
    • 1970-01-01
    • 2013-10-14
    • 1970-01-01
    • 2018-09-06
    • 1970-01-01
    • 2014-06-25
    相关资源
    最近更新 更多