Option name | Type | Description |
---|---|---|
scopeEl | Element | root element inside which DOM will be scanned and bound |
rootScope | Scope | Optional Root scope object where top level components will be saved. |
bindRootElement | Boolean | If set to false, then the root element will not be bound. True by default. |
throwOnErrors | Boolean | If set to false, then errors will only be logged to console. True by default. |
return | Scope |
milo.binder
Recursively scans the document tree inside scopeEl
(document.body by default) looking for ml-bind attribute that should contain the class, additional facets and the name of the component that should be created and bound to the element.
Possible values of ml-bind attribute:
:myView
- only component name. An instance of Component class will be created without any facets.View:myView
- class and component name. An instance of View class will be created.[Events, Data]:myView
- facets and component name. An instance of Component class will be created with the addition of facets Events and Data.View[Events, Data]:myView
- class, facet(s) and component name. An instance of View class will be created with the addition of facets Events and Data.Function returns an instance of Scope
class containing all components created as a result of scanning DOM.
If the component has Container
facet, children of this element will be stored in the scope
object, available as scope property on the Container facet of this component. Names of components within the scope should be unique, but they can be the same as the names of components in outer scope (or some other scope).
function binder(scopeEl, rootScope, bindRootElement, throwOnErrors) {
return createBinderScope(scopeEl, function(scope, el, attr, throwOnErrors) {
var info = new ComponentInfo(scope, el, attr, throwOnErrors);
return Component.create(info, throwOnErrors);
}, rootScope, bindRootElement, throwOnErrors);
}
// bind in two passes
function twoPass(scopeEl, rootScope, bindRootElement, throwOnErrors) {
var scanScope = binder.scan(scopeEl, rootScope, bindRootElement, throwOnErrors);
return binder.create(scanScope, undefined, throwOnErrors);
}
// scan DOM for BindAttribute
function scan(scopeEl, rootScope, bindRootElement, throwOnErrors) {
return createBinderScope(scopeEl, function(scope, el, attr, throwOnErrors) {
return new ComponentInfo(scope, el, attr, throwOnErrors);
}, rootScope, bindRootElement, throwOnErrors);
}
// create bound components
function create(scanScope, hostObject, throwOnErrors) {
var scope = new Scope(scanScope._rootEl, hostObject)
, addMethod = throwOnErrors === false ? '_safeAdd' : '_add';
scanScope._each(function(compInfo) {
// set correct component's scope
var info = _.clone(compInfo);
info.scope = scope;
// create component
var aComponent = Component.create(info, throwOnErrors);
scope[addMethod](aComponent, aComponent.name);
if (aComponent.container)
aComponent.container.scope = create(compInfo.container.scope, aComponent.container, throwOnErrors);
});
return scope;
}
Option name | Type | Description |
---|---|---|
scopeEl | Element | scopeEl root element inside which DOM will be scanned and bound (document.body by default). |
scopeObjectFactory | Function | See binder |
rootScope | Scope | Optional Root scope object where top level components will be saved. |
bindRootElement | Boolean | If set to false, then the root element will not be bound. True by default. |
throwOnErrors | Boolean | If set to false, then errors will only be logged to console. True by default. |
return | Scope | [description] |
createBinderScope
function createBinderScope(scopeEl, scopeObjectFactory, rootScope, bindRootElement, throwOnErrors) {
scopeEl = scopeEl || document.body;
var scope = rootScope || new Scope(scopeEl)
, addMethod = throwOnErrors === false ? '_safeAdd' : '_add';
createScopeForElement(scope, scopeEl, bindRootElement);
return scope;
function createScopeForElement(scope, el, bindRootElement) {
// get element's binding attribute (ml-bind by default)
var attr = new BindAttribute(el);
// if element has bind attribute crate scope object (Component or ComponentInfo)
if (attr.node && bindRootElement !== false) {
var scopedObject = scopeObjectFactory(scope, el, attr, throwOnErrors)
, isContainer = typeof scopedObject != 'undefined' && scopedObject.container;
}
// if there are childNodes add children to new scope if this element has component with Container facet
// otherwise create a new scope
if (el.childNodes && el.childNodes.length) {
if (isContainer) {
var innerScope = new Scope(el);
scopedObject.container.scope = innerScope;
innerScope._hostObject = scopedObject.container;
}
createScopeForChildren(el, isContainer ? innerScope : scope);
}
// if scope wasn't previously created on container facet, create empty scope anyway
if (isContainer && ! scopedObject.container.scope)
scopedObject.container.scope = new Scope(el);
// TODO condition after && is a hack, should not be used!
if (scopedObject) // && ! scope[attr.compName])
scope[addMethod](scopedObject, attr.compName);
// _.defer(postChildrenBoundMessage, el);
postChildrenBoundMessage(el);
return scopedObject;
function postChildrenBoundMessage(el) {
var elComp = Component.getComponent(el);
if (elComp)
elComp.postMessageSync('childrenbound');
}
}
function createScopeForChildren(containerEl, scope) {
var children = utilDom.children(containerEl);
_.forEach(children, function(node) {
createScopeForElement(scope, node, true);
});
return scope;
}
}