BackboneJS - 监听嵌套的模型更改

时间:2012-08-26 03:18:51

标签: javascript backbone.js

简短版:我有一个Backbone模型,它有一个属性,第二个Backbone模型。第一个模型中的一个函数改变了第二个模型的状态,但是我的视图(正在监听第一个模型的更改)似乎没有获得第二个模型的状态,尽管任何数量的日志记录都表示否则(我一直在各个点记录this以确认范围等)。我该如何解决这个问题?

长版本:我有一个代表学术课程的Backbone模型Course和一个模型NameList,它代表了一个Course注册的学生列表{1}}。 Course“有一个”NameListNameList由服务器上的单个文本文件支持。

我想在名为Course的{​​{1}}模型中创建一个函数,该模型会创建一个新的importNameList模型并导致NameList模型转到NameList它来自后端的数据。自我看来,fetch正在倾听CourseView模型的更改,而Course模型有Course,看起来这应该相应地更新视图。我的问题是它没有。

我想做什么

NameList

我必须做什么

var course = new Course();
var courseView = new CourseView({model : course});
courseView.model.importNameList('students.txt'); // Will cause view to re-render

这是我的日志记录代码。模型扩展var course = new Course(); // same var courseView = new CourseView({model : course}); // same courseView.model.importNameList('students.txt'); // same courseView.render(); // Argh, manually calling this is ugly. 只是因为我认为它可以解决我的问题,但事实并非如此。

控制台输出

Backbone.DeepModel

Course.js

[console] var course = new Course();
[console] var courseView = new CourseView({model : course});
[console] course.importNameList('students.txt');

          Course >>> importNameList
          NameList >>> initialize()
          NameList >>> fetch()
          GET http://localhost/files/students.txt 200 OK 16ms    
          CourseView >>> render() // Render starts before fetch completes
          CourseView <<< render() // Render finishes before fetch completes
          Course <<< importNameList
          NameList <<< fetch()

[console] courseView.render();

          CourseView >>> render()
          alice
          bob
          charlie
          dan
          erica
          fred
          george
          CourseView <<< render()

NameList.js

var Course = Backbone.DeepModel.extend({

    defaults : {
        nameList : new NameList()
    },

    initialize: function(options) {

        if (options && options.nameList) {
            this.set({nameList : options.nameList});
        }

    },

    importNameList : function(fileName) {

        console.log("Course >>> importNameList");
        var nameList = new NameList({fileName : fileName});
        this.set({nameList : nameList});
        console.log("Course <<< importNameList");
    }
});

CourseView.js

var NameList = Backbone.DeepModel.extend({

    defaults : {
        fileName : 'new.txt',
        names : []
    },

    initialize: function(options) {

        console.log("NameList >>> initialize()");
        var model = this;

        if (options && options.fileName) {
            model.set({fileName : options.fileName});
        }
        model.fetch();
    },

    fetch : function() {
        console.log("NameList >>> fetch()");
        var model = this;
        $.ajax({
            url : '/files/' + model.get('fileName'),
            success : function(response) {
                model.set({names : response.split('\n')});
                console.log("NameList <<< fetch()");
            }
        });
    }
});

2 个答案:

答案 0 :(得分:0)

快速回答是使用jQuery deferred对象。您可以在this article中找到有关使用backbone.js的延迟的更多信息。

我会添加更具体的信息,但我不清楚你为什么要重写fetch,这似乎是灾难的秘诀。我会坚持使用backbone的原始fetch方法并将其从NameList的初始化程序中删除,而不是从Course.js调用它,并使用从它返回的延迟来保证在获取完成后运行后面的步骤。

<强>更新

这是你可以做到这一点的一种方式的草图。首先从model.fetch();的{​​{1}}方法中取出initialize行,然后在NameList中调用它,使用其返回值(延迟对象)作为返回值importNameList

importNameList

现在我们从var nameList = new NameList({fileName : fileName}); this.set({nameList : nameList}); return nameList.fetch() 返回延迟,我们可以使用它来保证在渲染之前完成提取:

importNameList

我认为应该做你想要的,虽然我还没有真正测试过它。

答案 1 :(得分:0)

我最终做了以下事情。

<强> CourseView.js:

// No changes.

在Course.js中:

importNameList : function(name) {

    var model = this;
    var nameList = new NameList({fileName : fileName});
    // Set the attribute silently (without triggering a change event)
    model.set({nameList : nameList}, {silent: true});
    nameList.fetch({
        success : function() {
            model.change(); // Manually fire a change event when fetch completes.
        }
    });
}

在NameList.js中:

fetch : function(options) {
    var model = this;
    $.ajax({
        url : '/files/' + model.get('fileName'),
        success : function(response) {
            model.set({lines : response.split('\n')});
            // Make the "success" callback.
            options.success();
        }
    });
}