You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
270 lines
7.5 KiB
270 lines
7.5 KiB
define(["require", "dojo/when", "dojo/on", "dojo/dom-attr", "dojo/dom-style", "dojo/_base/declare", "dojo/_base/lang", |
|
"dojo/Deferred", "./utils/model", "./utils/constraints"], |
|
function(require, when, on, domAttr, domStyle, declare, lang, Deferred, model, constraints){ |
|
return declare("dojox.app.ViewBase", null, { |
|
// summary: |
|
// View base class with model & controller capabilities. Subclass must implement rendering capabilities. |
|
constructor: function(params){ |
|
// summary: |
|
// Constructs a ViewBase instance. |
|
// params: |
|
// view parameters, include: |
|
// |
|
// - app: the app |
|
// - id: view id |
|
// - name: view name |
|
// - parent: parent view |
|
// - controller: view controller module identifier |
|
// - children: children views |
|
this.id = ""; |
|
this.name = ""; |
|
this.children = {}; |
|
this.selectedChildren = {}; |
|
this.loadedStores = {}; |
|
// private |
|
this._started = false; |
|
lang.mixin(this, params); |
|
// mixin views configuration to current view instance. |
|
if(this.parent.views){ |
|
lang.mixin(this, this.parent.views[this.name]); |
|
} |
|
}, |
|
|
|
// start view |
|
start: function(){ |
|
// summary: |
|
// start view object. |
|
// load view template, view controller implement and startup all widgets in view template. |
|
if(this._started){ |
|
return this; |
|
} |
|
this._startDef = new Deferred(); |
|
when(this.load(), lang.hitch(this, function(){ |
|
// call setupModel, after setupModel startup will be called after startup the loadViewDeferred will be resolved |
|
this._createDataStore(this); |
|
this._setupModel(); |
|
})); |
|
return this._startDef; |
|
}, |
|
|
|
load: function(){ |
|
var vcDef = this._loadViewController(); |
|
when(vcDef, lang.hitch(this, function(controller){ |
|
if(controller){ |
|
//lang.mixin(this, controller); |
|
declare.safeMixin(this, controller); |
|
} |
|
})); |
|
return vcDef; |
|
}, |
|
|
|
_createDataStore: function(){ |
|
// summary: |
|
// Create data store instance for View specific stores |
|
// |
|
// TODO: move this into a common place for use by main and ViewBase |
|
// |
|
if(this.parent.loadedStores){ |
|
lang.mixin(this.loadedStores, this.parent.loadedStores); |
|
} |
|
|
|
if(this.stores){ |
|
//create stores in the configuration. |
|
for(var item in this.stores){ |
|
if(item.charAt(0) !== "_"){//skip the private properties |
|
var type = this.stores[item].type ? this.stores[item].type : "dojo/store/Memory"; |
|
var config = {}; |
|
if(this.stores[item].params){ |
|
lang.mixin(config, this.stores[item].params); |
|
} |
|
// we assume the store is here through dependencies |
|
try{ |
|
var storeCtor = require(type); |
|
}catch(e){ |
|
throw new Error(type+" must be listed in the dependencies"); |
|
} |
|
if(config.data && lang.isString(config.data)){ |
|
//get the object specified by string value of data property |
|
//cannot assign object literal or reference to data property |
|
//because json.ref will generate __parent to point to its parent |
|
//and will cause infinitive loop when creating StatefulModel. |
|
config.data = lang.getObject(config.data); |
|
} |
|
if(this.stores[item].observable){ |
|
try{ |
|
var observableCtor = require("dojo/store/Observable"); |
|
}catch(e){ |
|
throw new Error("dojo/store/Observable must be listed in the dependencies"); |
|
} |
|
this.stores[item].store = observableCtor(new storeCtor(config)); |
|
}else{ |
|
this.stores[item].store = new storeCtor(config); |
|
} |
|
this.loadedStores[item] = this.stores[item].store; // add this store to loadedStores for the view |
|
} |
|
} |
|
} |
|
}, |
|
|
|
_setupModel: function(){ |
|
// summary: |
|
// Load views model if it is not already loaded then call _startup. |
|
// tags: |
|
// private |
|
|
|
if(!this.loadedModels){ |
|
var createPromise; |
|
try{ |
|
createPromise = model(this.models, this.parent, this.app); |
|
}catch(e){ |
|
throw new Error("Error creating models: "+e.message); |
|
} |
|
when(createPromise, lang.hitch(this, function(models){ |
|
if(models){ |
|
// if models is an array it comes from dojo/promise/all. Each array slot contains the same result object |
|
// so pick slot 0. |
|
this.loadedModels = lang.isArray(models)?models[0]:models; |
|
} |
|
this._startup(); |
|
}), |
|
function(err){ |
|
throw new Error("Error creating models: "+err.message); |
|
}); |
|
}else{ // loadedModels already created so call _startup |
|
this._startup(); |
|
} |
|
}, |
|
|
|
_startup: function(){ |
|
// summary: |
|
// startup widgets in view template. |
|
// tags: |
|
// private |
|
|
|
this._initViewHidden(); |
|
this._needsResize = true; // flag used to be sure resize has been called before transition |
|
|
|
this._startLayout(); |
|
}, |
|
|
|
_initViewHidden: function(){ |
|
domStyle.set(this.domNode, "visibility", "hidden"); |
|
}, |
|
|
|
_startLayout: function(){ |
|
// summary: |
|
// startup widgets in view template. |
|
// tags: |
|
// private |
|
this.app.log(" > in app/ViewBase _startLayout firing layout for name=[",this.name,"], parent.name=[",this.parent.name,"]"); |
|
|
|
if(!this.hasOwnProperty("constraint")){ |
|
this.constraint = domAttr.get(this.domNode, "data-app-constraint") || "center"; |
|
} |
|
constraints.register(this.constraint); |
|
|
|
|
|
this.app.emit("app-initLayout", { |
|
"view": this, |
|
"callback": lang.hitch(this, function(){ |
|
//start widget |
|
this.startup(); |
|
|
|
// call view assistant's init() method to initialize view |
|
this.app.log(" > in app/ViewBase calling init() name=[",this.name,"], parent.name=[",this.parent.name,"]"); |
|
this.init(); |
|
this._started = true; |
|
if(this._startDef){ |
|
this._startDef.resolve(this); |
|
} |
|
}) |
|
}); |
|
}, |
|
|
|
|
|
_loadViewController: function(){ |
|
// summary: |
|
// Load view controller by configuration or by default. |
|
// tags: |
|
// private |
|
// |
|
var viewControllerDef = new Deferred(); |
|
var path; |
|
|
|
if(!this.controller){ // no longer using this.controller === "none", if we dont have one it means none. |
|
this.app.log(" > in app/ViewBase _loadViewController no controller set for view name=[",this.name,"], parent.name=[",this.parent.name,"]"); |
|
viewControllerDef.resolve(true); |
|
return viewControllerDef; |
|
}else{ |
|
path = this.controller.replace(/(\.js)$/, ""); |
|
} |
|
|
|
var requireSignal; |
|
try{ |
|
var loadFile = path; |
|
var index = loadFile.indexOf("./"); |
|
if(index >= 0){ |
|
loadFile = path.substring(index+2); |
|
} |
|
requireSignal = require.on ? require.on("error", function(error){ |
|
if(viewControllerDef.isResolved() || viewControllerDef.isRejected()){ |
|
return; |
|
} |
|
if(error.info[0] && (error.info[0].indexOf(loadFile) >= 0)){ |
|
viewControllerDef.resolve(false); |
|
if(requireSignal){ |
|
requireSignal.remove(); |
|
} |
|
} |
|
}) : null; |
|
|
|
if(path.indexOf("./") == 0){ |
|
path = "app/"+path; |
|
} |
|
|
|
require([path], function(controller){ |
|
viewControllerDef.resolve(controller); |
|
if(requireSignal){ |
|
requireSignal.remove(); |
|
} |
|
}); |
|
}catch(e){ |
|
viewControllerDef.reject(e); |
|
if(requireSignal){ |
|
requireSignal.remove(); |
|
} |
|
} |
|
return viewControllerDef; |
|
}, |
|
|
|
init: function(){ |
|
// summary: |
|
// view life cycle init() |
|
}, |
|
|
|
beforeActivate: function(){ |
|
// summary: |
|
// view life cycle beforeActivate() |
|
}, |
|
|
|
afterActivate: function(){ |
|
// summary: |
|
// view life cycle afterActivate() |
|
}, |
|
|
|
beforeDeactivate: function(){ |
|
// summary: |
|
// view life cycle beforeDeactivate() |
|
}, |
|
|
|
afterDeactivate: function(){ |
|
// summary: |
|
// view life cycle afterDeactivate() |
|
}, |
|
|
|
destroy: function(){ |
|
// summary: |
|
// view life cycle destroy() |
|
} |
|
}); |
|
});
|
|
|