Option name | Type | Description |
---|---|---|
data | Object,Array | optional initial array data. If it is planned to connect model to view it is usually better to instantiate an empty Model ( |
hostObject | Object | optional object that hosts model on one of its properties. Can be used when model itself is the context of the message subscriber and you need to travers to this object (although it is possible to set any context). Can also be used to proxy model's methods to the host like Model facet is doing. |
options | Object | pass { reactive: false } to use model without messaging when it is not needed - it makes it much faster |
return | Model |
milo.Model
Model class instantiates objects that allow deep data access with safe getters that return undefined (rather than throwing exception) when properties/items of unexisting objects/arrays are requested and safe setters that create object trees when properties/items of unexisting objects/arrays are set and also post messages to allow subscription on changes and enable data reactivity.
Reactivity is implememnted via Connector that can be instantiated either directly or with more convenient interface of milo.minder. At the moment model can be connected to Data facet or to another model or ModelPath.
Model constructor returns objects that are functions at the same time; when called they return ModelPath objects that allow get/set access to any point in model data. See ModelData below.
You can subscribe to model changes with on
method by passing model access path in place of message, pattern or string with any number of stars to subscribe to a certain depth in model (e.g., '***'
to subscribe to three levels).
function Model(data, hostObject, options) {
// `model` will be returned by constructor instead of `this`. `model`
// (`modelPath` function) should return a ModelPath object with "synthesized" methods
// to get/set model properties, to subscribe to property changes, etc.
// Additional arguments of modelPath can be used in the path using interpolation - see ModelPath below.
var model = function modelPath(accessPath) { // , ... arguments that will be interpolated
return Model$path.apply(model, arguments);
};
model.__proto__ = Model.prototype;
model._hostObject = hostObject;
model._options = options || {};
if (model._options.reactive !== false) {
model._prepareMessengers();
// subscribe to "changedata" message to enable reactive connections
model.onSync('changedata', changeDataHandler);
}
if (data) model._data = data;
return model;
}
Model.prototype.__proto__ = Model.__proto__;
_.extendProto(Model, {
path: Model$path,
get: Model$get,
proxyMessenger: proxyMessenger, // deprecated, should not be used
proxyMethods: proxyMethods,
_prepareMessengers: _prepareMessengers,
_getHostObject: _getHostObject,
destroy: Model$destroy
});
// set, del, splice are added to model
_.extendProto(Model, synthesize.modelMethods);
milo.Model.Path
_.extend(Model, {
Path: ModelPath,
useWith: Model$$useWith,
_utils: {
path: pathUtils,
model: modelUtils,
changeDataHandler: changeDataHandler
}
});
Expose Messenger methods on Facet prototype
var MESSENGER_PROPERTY = '_messenger';
Messenger.useWith(Model, MESSENGER_PROPERTY, Messenger.defaultMethods);
ModelPath methods added to Model prototype
['len', 'push', 'pop', 'unshift', 'shift'].forEach(function(methodName) {
var method = ModelPath.prototype[methodName];
_.defineProperty(Model.prototype, methodName, method);
});
Model instance method.
Get model data.
function Model$get() {
return this._data;
}
Option name | Type | Description |
---|---|---|
accessPath | String | string that defines path to access model. Path string consists of parts to define either property access ( |
arguments | List | additional arguments of this method can be used to create interpolated paths. E.g. |
return | ModelPath |
Model instance method.
Returns ModelPath object that implements the same API as model but allows access to any point inside model as defined by accessPath
.
See ModelPath class for more information.
function Model$path(accessPath) { // , ... arguments that will be interpolated
if (! accessPath) return this;
// "null" is context to pass to ModelPath, first parameter of bind
// "this" (model) is added in front of all arguments
_.splice(arguments, 0, 0, null, this);
// calling ModelPath constructor with new and the list of arguments: this (model), accessPath, ...
return new (Function.prototype.bind.apply(ModelPath, arguments));
}
Option name | Type | Description |
---|---|---|
modelHostObject | Object | optional host object. If not passed, hostObject passed to Model constructor will be used. |
Model instance method.
Proxy model's Messenger methods to host object.
function proxyMessenger(modelHostObject) {
modelHostObject = modelHostObject || this._hostObject;
Mixin.prototype._createProxyMethods.call(this[MESSENGER_PROPERTY], Messenger.defaultMethods, modelHostObject);
}
var modelMethodsToProxy = ['path', 'get', 'set', 'del', 'splice', 'len', 'push', 'pop', 'unshift', 'shift'];
Option name | Type | Description |
---|---|---|
hostClass | Function | |
instanceKey | type | |
mixinMethods | type | optional |
Expose model methods on
See same method in Mixin class for parameters meaning
function Model$$useWith(hostClass, instanceKey, mixinMethods) {
mixinMethods = mixinMethods || modelMethodsToProxy;
Mixin.useWith.call(Model, hostClass, instanceKey, mixinMethods);
}
Option name | Type | Description |
---|---|---|
modelHostObject | Object | optional host object. If not passed, hostObject passed to Model constructor will be used. |
Model instance method.
Proxy model methods to host object.
function proxyMethods(modelHostObject) {
modelHostObject = modelHostObject || this._hostObject;
Mixin.prototype._createProxyMethods.call(this, modelMethodsToProxy, modelHostObject);
}
Model instance method.
Create and connect internal and external model's messengers.
External messenger's methods are proxied on the model and they allows "*" subscriptions.
function _prepareMessengers() {
// model will post all its changes on internal messenger
var internalMessenger = new Messenger(this, undefined, undefined);
// message source to connect internal messenger to external
var internalMessengerSource = new MessengerMessageSource(this, undefined, new ModelMsgAPI, internalMessenger);
// external messenger to which all model users will subscribe,
// that will allow "*" subscriptions and support "changedata" message api.
var externalMessenger = new Messenger(this, undefined, internalMessengerSource);
_.defineProperty(this, MESSENGER_PROPERTY, externalMessenger);
_.defineProperty(this, '_internalMessenger', internalMessenger);
}
function _getHostObject() {
return this._hostObject;
}
function Model$destroy() {
this[MESSENGER_PROPERTY].destroy();
this._internalMessenger.destroy();
this._destroyed = true;
}