milo

Container

declaration
 Container 

milo.registry.facets.get('Container')
A special component facet that makes component create its own inner scope.
When milo.binder binds DOM tree and creates components, if components are inside component WITH Container facet, they are put on the scope of it (component.container.scope - see Scope), otherwise they are put on the same scope even though they may be deeper in DOM tree.
It allows creating namespaces avoiding components names conflicts, at the same time creating more shallow components tree than the DOM tree.
To create components for elements inside the current component use:

component.container.binder();

See milo.binder

var Container = _.createSubclass(ComponentFacet, 'Container');

####Container facet instance methods####

  • binder - create components from DOM inside the current one
_.extendProto(Container, {
    start: Container$start,
    path: Container$path,
    getState: Container$getState,
    setState: Container$setState,
    binder: Container$binder,
    destroy: Container$destroy,
    unwrap: Container$unwrap,

    append: Container$append,
    insertBefore: Container$insertBefore,
    remove: Container$remove
});

facetsRegistry.add(Container);

module.exports = Container;

Container$binder

function
 Container$binder() 

Container instance method.
Scans DOM, creates components and adds to scope children of component element.

function Container$binder() {
    return miloBinder(this.owner.el, this.scope, false);
}

Container$start

function
 Container$start() 

Container instance method.
Setup empty scope object on start

function Container$start() {
    ComponentFacet.prototype.start.apply(this, arguments);
    this.scope = new Scope(this.owner.el, this);
}


var allowedNamePattern = /^[A-Za-z][A-Za-z0-9\_\$]*$/;

Container$path

function
 Container$path() 

Option name Type Description
path String

path of child component in scope, each name should be prefixed with '.', e.g.: '.child.subchild'

return Component

Container instance method.
Safely traverses component scope
Returns component in scope for a given path
If path is invalid the method will throw, if there is no component at a given path or some of the components along the path does not have Container facet the method will return undefined,

function Container$path(path) {
    path = path.split('.');
    var len = path.length;
    if (path[0] || len < 2) throwInvalidPath();
    var comp = this.owner;
    for (var i = 1; i < len; i++) {
        var name = path[i];
        if (!allowedNamePattern.test(name)) throwInvalidPath();
        if (!comp.container) return;
        comp = comp.container.scope[name];
        if (!comp) return;
    }
    return comp;

    function throwInvalidPath() {
        throw new Error('path ' + path + ' is invalid');
    }
}

Container$getState

function
 Container$getState() 

Option name Type Description
deepCopy Boolean

true by default

return Object

Container instance method
Called by Component.prototype.getState to get facet's state
Returns the state of components in the scope

function Container$getState(deepCopy) {
    var state = { scope: {} };
    if (deepCopy !== false)
        this.scope._each(function(component, compName) {
            state.scope[compName] = component._getState();
        });
    return state;
}

Container$setState

function
 Container$setState() 

Option name Type Description
data Object

data to set on facet's model

Container instance method
Called by Component.prototype.setState to set facet's state
Sets the state of components in the scope

function Container$setState(state) {
    _.eachKey(state.scope, function(compData, compName) {
        var component = this.scope[compName];
        if (component)
            component.setState(compData);
        else
            logger.warn('component "' + compName + '" does not exist on scope');
    }, this);
}

function Container$destroy(opts) {
    this.scope._each(function(component) {
        if (opts.async) _.deferMethod(component, 'destroy', opts);
        else component.destroy(opts);
    });
    this.scope._detachElement();
    ComponentFacet.prototype.destroy.apply(this, arguments);
}

Container$unwrap

function
 Container$unwrap() 

Option name Type Description
renameChildren Boolean

pass false to not rename scope children (default is true)

destroy Boolean

If not false, the component will be destroyed at the end (default is true).

Container instance method
Moves all of the contents of the owner into the parent scope

function Container$unwrap(renameChildren, destroy) {
    domUtils.unwrapElement(this.owner.el);
    if (this.scope)
        this.scope._each(function (child) {
            child.remove();
            if (renameChildren !== false) child.rename(undefined, false);
            if (this.owner.scope) this.owner.scope._add(child);
        }, this);
    if (destroy !== false) this.owner.destroy();
}

Container$append

function
 Container$append() 

Option name Type Description
comp Component

component that will be appended

Container instance method
Append component to DOM and to scope

function Container$append(comp) {
    this.scope._add(comp);
    this.owner.el.appendChild(comp.el);
}

Container$insertBefore

function
 Container$insertBefore() 

Option name Type Description
comp Component

component that will be inserted

sibling Component

component before which component will be appended

Container instance method
Insert component to DOM and to scope before another component

function Container$insertBefore(comp, sibling) {
    this.scope._add(comp);
    this.el.insertBefore(comp.el, sibling && sibling.el);
}

function Container$remove(comp) {
    this.scope._remove(comp);
    this.owner.el.removeChild(comp.el);
}