假设我有以下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()
?
我知道通过返回一个单例对象可以做到这一点,但如果我想保留构造函数,我能做到吗?
另一个问题是,如果要获得新实例,在上面的代码中激活和停用是什么意思,因此保证了一个“干净”的对象呢?
答案 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方法一起使用,只是我们只是传递一个消息通道,而不是数据本身。
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是模块的规范名称:
有[至少]两种方法可以为实例提供服务:通过Durandal组合,传入activationData
对象,以及使用RequireJS进行依赖注入。
(另一种方法是在请求 - 响应场景下使用客户端消息总线,例如postal.js。但那是一匹不同颜色的马。)
如果您不熟悉这些技巧,我可以详细说明您的意愿。