Durandal构造函数但仍想记住数据

时间:2014-05-09 20:06:43

标签: javascript durandal durandal-2.0

假设我有以下durandal代码:

define(['durandal/app', 'plugins/http'], function (app) {
    var vm = function(){
        var self = this;
        self.myData = ko.observableArray([]);
        }

    };
    vm.activate = function() {
        this.myData([]);
    };
    vm.deactivate = function(){

    };
    return vm;
};

我知道通过返回构造函数,每次激活视图模型时,它都会 返回一个新实例。

我的问题是:无论如何,当我访问时,如果之前的访问权限为myData(),那么我不想设置this.myData([]);,而是使用之前的myData()

我知道通过返回一个单例对象可以做到这一点,但如果我想保留构造函数,我能做到吗?

另一个问题是,如果要获得新实例,在上面的代码中激活和停用是什么意思,因此保证了一个“干净”的对象呢?

2 个答案:

答案 0 :(得分:4)

您可以采取以下几种方法,我将在下面列举。

对于以下所有示例,请考虑 Projects 是首选主题。

注意(无视图)

通过这种方法,我们将ProjectsServices模块(单例)注入到Projects模块(实例)中。但是这种方法只有在ProjectsServices不提供一个或多个视图时才有效。下面,如果我们的服务模块本身提供一个或多个视图,我将向您展示我们可以做些什么。

ProjectsServices ViewModel(singleton) sevicesProjects.js

define('projectsServices', [],
    function() {
        var myData = null; //this may or may not be an observable

        return {
            myData: myData  //we return an object literal, which is what makes this module a singleton
        };
    }
);

项目ViewModel(实例) projects.js

define('projects', ['projectsServices'],
    function(services) {

        //Constructor
        var Projects = function () {
            this.myData = ko.observable(); //this may or may not be an observable
        };

        Projects.prototype.activate = function (activationData) {
           this.myData(services.myData);
        };

        Projects.prototype.detached = function () {
            services.myData = this.myData();  /store back to the services module for later use
        };

        return Projects; //we return a constructor function, which is what makes this module an instance
    }
);

HOST-CLIENT(VIEW)

使用这种方法,Projects模块由ProjectsServices模块组成,我们通过myData上的一个observable来回传递activationData。此外,该方法假设服务模块不仅提供代码服务,还提供视图服务。弹出其他表单的全局“添加联系人”表单是基于视图的服务模块的示例。当然,“添加联系人”视图后面会有一个viewModel,它代表添加联系人的代码服务。

ProjectsServices ViewModel(singleton) servicesProjects.js

define('projectsServices', [],
    function() {
        var myData = ko.observable(); //should be an observable            
    }
);

ProjectsServices View servicesProjects.html

/*We bring in the Projects module through composition and pass in the observable, `myData`, itself (not `myData()`, but `myData`)*/
<div>
    <div data-bind="compose: {model: 'viewmodels/projects', activationData: myData}">
    </div>
</div>

项目ViewModel(实例) projects.js

define('projects', [],
    function() {

        //Constructor
        var Projects = function () {
            this.myDataLocal = ko.observable(); //this may or may not be an observable
            this.myDataFromServices = null;
        };

        Projects.prototype.activate = function (activationData) {
            this.myDataFromServices = activationData
            this.myDataLocal(activationData());
        };

        Projects.prototype.detached = function () {
            this.myDataFromServices(this.myDataLocal());  /store back to the services module for later use
        };

        return Projects; //we return a constructor function, which is what makes this module an instance
    }
);

项目视图 projects.html

/*There are infinite number of ways to bind to your myDataLocal*/
<div>
    <div data-bind="text: myDataLocal}">
    </div>
</div>

发布 - 订阅

通过这种方法,我们通过app利用Durandal的内置发布/订阅设施。这种方法可以与上面给出的Injection或Host-Client一起使用。策略是从实例模块的activate处理程序发布请求消息,并在同一个处理程序中接收回复消息,这两个消息的目的是请求和提供myData(已保存早些时候,大概是)。当我们准备将myData保存回服务模块时,我们会发送另一条消息myData作为有效负载。

ProjectsServices ViewModel(singleton) servicesProjects.js

define('projectsServices', ['durandal/app'],
    function(app) {
        var 
            myData = null, //this may or may not be an observable

            activate = function () {
                app.on('requestForMyData').then( function () {
                    app.trigger('responseMyData', myData);
                });
                app.on('storeMyData').then( function (data) {
                    myData = data; //where 'data' is the payload
                });
            },

            detached = function () {
                app.off('requestForMyData');
                app.off('storeMyData');
            };

        return {
            myData: myData,  //we return an object literal, which is what makes this module a singleton
            activate: activate,
            detached: detached
        };
    }
);

项目ViewModel(实例) projects.js

define('projects', ['durandal/app'],
    function(app) {

        //Constructor
        var Projects = function () {
            this.myData = ko.observable(); //this may or may not be an observable
        };

        Projects.prototype.activate = function () {
            var that = this;
            app.on('responseMyData').then( function (data) {
                that.myData(data);
            });
            app.trigger('requestForMyData'); //no payload
        };

        Projects.prototype.detached = function () {
            app.trigger('storeMyData', this.myData());
            app.off('responseMyData');
        };

        return Projects; //we return a constructor function, which is what makes this module an instance
    }
);

在这种情况下,视图不会改变,因此这里不提供它们。

MESSAGE BUS

这种方法实际上与pub-sub方法完全相同,只是我们使用的是客户端消息总线,例如postal.js。正如您将看到的,它更加精致。它也恰好是我们在生产中采用的方法。这种方法应该与上面的Host-Client方法一起使用,只是我们只是传递一个消息通道,而不是数据本身。

  • 您可以下载postal.js here
  • 在Github hereherehere上查看我与Jim Cowart(@ifandelse)的交流(@estaylorco)。
  • 查看postal.request-response的RC(我在下面使用使用,因为它在预览中),以及我在那里的交换。 postal.request-response是我所要求的(双向频道),它只适用于您正在使用的场景。它极大地简化了请求 - 响应方案。

ProjectsServices ViewModel(singleton) servicesProjects.js

define('projectsServices', ['postal'],
    function(postal) {
        var 
            outletMessageChannel = 'someuniqueidentifier',
            subscriptions = [],
            myData = null, //this may or may not be an observable

            activate = function () {
                var that = this;
                subscriptions.push(postal.subscribe({
                  channel: outletMessageChannel,
                  topic: 'request.mydata',
                  callback: function () {
                     postal.publish({
                         channel: outletMessageChannel,
                         topic: 'response.mydata',
                         data: that.myData
                     });
                  }
                }));
                subscriptions.push(postal.subscribe({
                  channel: outletMessageChannel,
                  topic: 'store.mydata',
                  callback: function (data) {
                      that.myData = data;   
                  }
                }));
            },

            detached = function () {
                //I'm using underscore.js here, but you can use any approach to iterate over subscriptions
                _.each(subscriptions, function(sub) {
                    sub.unsubscribe();
                    sub.callback = null;
                    sub = null;
                });
                subscriptions = null;
            };

        return {
            myData: myData,  //we return an object literal, which is what makes this module a singleton
            activate: activate,
            detached: detached
        };
    }
);

ProjectsServices View servicesProjects.html

/*We bring in the Projects module through composition and pass in the message channel*/
<div>
    <div data-bind="compose: {model: 'viewmodels/projects', activationData: outletMessageChannel}">
    </div>
</div>

项目ViewModel(实例) projects.js

define('projects', ['postal'],
    function(postal) {

        //Constructor
        var Projects = function () {
            this.subscriptions = [];
            this.outletMessageChannel = '';
            this.myData = ko.observable(); //this may or may not be an observable
        };

        Projects.prototype.activate = function (activationData) {
            this.outletMessageChannel = activationData;

            var that = this;

            subscriptions.push(postal.subscribe({
              channel: this.outletMessageChannel,
              topic: 'response.mydata',
              callback: function (data) {
                  that.myData(data);   
              }
            }));

            postal.publish({
                channel: this.outletMessageChannel,
                topic: 'request.mydata',
                data: null //no payload
            });              

        };

        Projects.prototype.detached = function () {
            postal.publish({
                channel: this.outletMessageChannel,
                topic: 'store.mydata',
                data: this.myData()
            }); 

            //I'm using underscore.js here, but you can use any approach to iterate over subscriptions
            _.each(this.subscriptions, function(sub) {
                sub.unsubscribe();
                sub.callback = null;
                sub = null;
            });
            this.subscriptions = null;
        };

        return Projects; //we return a constructor function, which is what makes this module an instance
    }
);

请注意,在这种情况下,“项目”视图不会更改,因此不会包含在此处。

答案 1 :(得分:0)

是的,你可以这样做。

例如,在我们的应用程序中,每个模块都有两个部分:遵循XxxServices命名约定的单例模块和一个名为Xxx的实例模块,其中Xxx是模块的规范名称:

  • ProjectsServices(singleton)
  • 项目(实例)

有[至少]两种方法可以为实例提供服务:通过Durandal组合,传入activationData对象,以及使用RequireJS进行依赖注入。

(另一种方法是在请求 - 响应场景下使用客户端消息总线,例如postal.js。但那是一匹不同颜色的马。)

如果您不熟悉这些技巧,我可以详细说明您的意愿。