1

简短版本:我有一个 Backbone 模型,它的属性之一是第二个 Backbone 模型。第一个模型中的一个函数改变了第二个模型的状态,但是我的视图,它正在监听第一个模型的变化,似乎没有得到第二个模型的状态,尽管有任何数量的日志记录表明不是这样(我一直 this在各个点登录以确认范围等)。我怎样才能解决这个问题?

长版:我有一个 Backbone 模型Course,它代表一门学术课程,还有一个模型NameList,它代表一个Course. 一个Course“有” NameList。ANameList由服务器上的单个文本文件支持。

我想在Course模型中调用一个函数,该函数importNameList创建一个新NameList模型并导致该NameList模型fetch及其来自后端的数据。由于我的观点CourseView是监听模型的变化Course,并且Course模型 has-a 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只是因为我认为它可以解决我的问题,但它没有。

控制台输出

[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()

课程.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");
    }
});

名称列表.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()");
            }
        });
    }
});

CourseView.js

var CourseView = Backbone.View.extend({

    initialize : function(options) {

        var view = this;
        if (options && options.model) {
            view.model = options.model;
        } else {
            view.model = new Course();
        }

        view.model.on('change', function() {
            view.render();
        });

    },

    render : function() {

        console.log("CourseView >>> render()");
        var names = this.model.get('nameList').get('names');
        for (var i = 0; i < names.length; i++) {
            console.log(names[i]);
        }
        console.log("CourseView <<< render()");
        return this;
    }

});
4

2 回答 2

0

快速的答案是使用 jQuery 一个deferred对象。您可以在本文中找到更多关于将延迟与主干.js 结合使用的信息。

我会添加更具体的信息,但我不太清楚你为什么要覆盖 fetch,这似乎是灾难的根源。我会坚持使用主干的原始 fetch 方法,并将其从 NameList 的初始化程序中删除,而是从 Course.js 调用它,并使用从它返回的 deferred 来保证在 fetch 完成后运行之后的步骤。

更新:

这是您可以执行此操作的一种方法的草图。首先从 的方法中取出这一model.fetch();行,然后在 中调用它,使用它的返回值(一个延迟对象)作为 的返回值:initializeNameListimportNameListimportNameList

var nameList = new NameList({fileName : fileName});
this.set({nameList : nameList});
return nameList.fetch()

现在我们返回了一个 deferred from importNameList,我们可以使用它来保证在渲染之前获取是完整的:

this.deferred = courseView.model.importNameList('students.txt');
this.deferred.done(function() { courseView.render() });

我认为这应该做你想要的,虽然我还没有实际测试过。

于 2012-08-26T03:36:57.327 回答
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();
        }
    });
}
于 2012-09-03T02:51:06.233 回答