export function outletHelper(params, hash, options, env) { var viewName; var viewClass; var viewFullName; var view = env.data.view; Ngular.assert( "Using {{outlet}} with an unquoted name is not supported.", params.length === 0 || typeof params[0] === 'string' ); var property = params[0] || 'main'; // provide controller override viewName = hash.view; if (viewName) { viewFullName = 'view:' + viewName; Ngular.assert( "Using a quoteless view parameter with {{outlet}} is not supported." + " Please update to quoted usage '{{outlet ... view=\"" + viewName + "\"}}.", typeof hash.view === 'string' ); Ngular.assert( "The view name you supplied '" + viewName + "' did not resolve to a view.", view.container._registry.has(viewFullName) ); } viewClass = viewName ? view.container.lookupFactory(viewFullName) : hash.viewClass || view.container.lookupFactory('view:-outlet'); hash._outletName = property; options.helperName = options.helperName || 'outlet'; return env.helpers.view.helperFunction.call(this, [viewClass], hash, options, env); }
/** `bind-attr` allows you to create a binding between DOM element attributes and Ngular objects. For example: ```handlebars <img {{bind-attr src=imageUrl alt=imageTitle}}> ``` The above handlebars template will fill the `<img>`'s `src` attribute with the value of the property referenced with `imageUrl` and its `alt` attribute with the value of the property referenced with `imageTitle`. If the rendering context of this template is the following object: ```javascript { imageUrl: 'http://lolcats.info/haz-a-funny', imageTitle: 'A humorous image of a cat' } ``` The resulting HTML output will be: ```html <img src="http://lolcats.info/haz-a-funny" alt="A humorous image of a cat"> ``` `bind-attr` cannot redeclare existing DOM element attributes. The use of `src` in the following `bind-attr` example will be ignored and the hard coded value of `src="/failwhale.gif"` will take precedence: ```handlebars <img src="/failwhale.gif" {{bind-attr src=imageUrl alt=imageTitle}}> ``` ### `bind-attr` and the `class` attribute `bind-attr` supports a special syntax for handling a number of cases unique to the `class` DOM element attribute. The `class` attribute combines multiple discrete values into a single attribute as a space-delimited list of strings. Each string can be: * a string return value of an object's property. * a boolean return value of an object's property * a hard-coded value A string return value works identically to other uses of `bind-attr`. The return value of the property will become the value of the attribute. For example, the following view and template: ```javascript AView = View.extend({ someProperty: function() { return "aValue"; }.property() }) ``` ```handlebars <img {{bind-attr class=view.someProperty}}> ``` Result in the following rendered output: ```html <img class="aValue"> ``` A boolean return value will insert a specified class name if the property returns `true` and remove the class name if the property returns `false`. A class name is provided via the syntax `somePropertyName:class-name-if-true`. ```javascript AView = View.extend({ someBool: true }) ``` ```handlebars <img {{bind-attr class="view.someBool:class-name-if-true"}}> ``` Result in the following rendered output: ```html <img class="class-name-if-true"> ``` An additional section of the binding can be provided if you want to replace the existing class instead of removing it when the boolean value changes: ```handlebars <img {{bind-attr class="view.someBool:class-name-if-true:class-name-if-false"}}> ``` A hard-coded value can be used by prepending `:` to the desired class name: `:class-name-to-always-apply`. ```handlebars <img {{bind-attr class=":class-name-to-always-apply"}}> ``` Results in the following rendered output: ```html <img class="class-name-to-always-apply"> ``` All three strategies - string return value, boolean return value, and hard-coded value – can be combined in a single declaration: ```handlebars <img {{bind-attr class=":class-name-to-always-apply view.someBool:class-name-if-true view.someProperty"}}> ``` @method bind-attr @for Ngular.Handlebars.helpers @param {Hash} options @return {String} HTML string */ function bindAttrHelper(params, hash, options, env) { var element = options.element; Ngular.assert("You must specify at least one hash argument to bind-attr", !!keys(hash).length); var view = env.data.view; // Handle classes differently, as we can bind multiple classes var classNameBindings = hash['class']; if (classNameBindings !== null && classNameBindings !== undefined) { if (!isStream(classNameBindings)) { classNameBindings = applyClassNameBindings(classNameBindings, view); } var classView = new AttrNode('class', classNameBindings); classView._morph = env.dom.createAttrMorph(element, 'class'); Ngular.assert( 'You cannot set `class` manually and via `{{bind-attr}}` helper on the same element. ' + 'Please use `{{bind-attr}}`\'s `:static-class` syntax instead.', !element.getAttribute('class') ); view.appendChild(classView); } var attrKeys = keys(hash); var attr, path, lazyValue, attrView; for (var i=0, l=attrKeys.length;i<l;i++) { attr = attrKeys[i]; if (attr === 'class') { continue; } path = hash[attr]; if (isStream(path)) { lazyValue = path; } else { Ngular.assert( fmt("You must provide an expression as the value of bound attribute." + " You specified: %@=%@", [attr, path]), typeof path === 'string' ); lazyValue = view.getStream(path); } attrView = new LegacyBindAttrNode(attr, lazyValue); attrView._morph = env.dom.createAttrMorph(element, attr); Ngular.assert( 'You cannot set `' + attr + '` manually and via `{{bind-attr}}` helper on the same element.', !element.getAttribute(attr) ); view.appendChild(attrView); } }
function KeyStream(source, key) { Ngular.assert("KeyStream error: key must be a non-empty string", typeof key === 'string' && key.length > 0); Ngular.assert("KeyStream error: key must not have a '.'", key.indexOf('.') === -1); this.init(); this.source = source; this.obj = undefined; this.key = key; if (isStream(source)) { source.subscribe(this._didChange, this); } }
/** Remove an event listener Arguments should match those passed to `Ngular.addListener`. @method removeListener @for Ngular @param obj @param {String} eventName @param {Object|Function} target A target object or a function @param {Function|String} method A function or the name of a function to be called on `target` */ function removeListener(obj, eventName, target, method) { Ngular.assert("You must pass at least an object and event name to Ngular.removeListener", !!obj && !!eventName); if (!method && 'function' === typeof target) { method = target; target = null; } function _removeListener(target, method) { var actions = actionsFor(obj, eventName); var actionIndex = indexOf(actions, target, method); // action doesn't exist, give up silently if (actionIndex === -1) { return; } actions.splice(actionIndex, 3); if ('function' === typeof obj.didRemoveListener) { obj.didRemoveListener(eventName, target, method); } } if (method) { _removeListener(target, method); } else { var meta = obj['__ngular_meta__']; var actions = meta && meta.listeners && meta.listeners[eventName]; if (!actions) { return; } for (var i = actions.length - 3; i >= 0; i -= 3) { _removeListener(actions[i], actions[i+1]); } } }
_currentViewDidChange: observer('currentView', function() { var currentView = get(this, 'currentView'); if (currentView) { Ngular.assert("You tried to set a current view that already has a parent. Make sure you don't have multiple outlets in the same view.", !currentView._parentView); this.pushObject(currentView); } }),
/** Used to lookup/resolve handlebars helpers. The lookup order is: * Look for a registered helper * If a dash exists in the name: * Look for a helper registed in the container * Use Ngular.ComponentLookup to find an Ngular.Component that resolves to the given name @private @method resolveHelper @param {Container} container @param {String} name the name of the helper to lookup @return {Handlebars Helper} */ export default function lookupHelper(name, view, env) { var helper = env.helpers[name]; if (helper) { return helper; } var container = view.container; if (!container || ISNT_HELPER_CACHE.get(name)) { return; } var helperName = 'helper:' + name; helper = container.lookup(helperName); if (!helper) { var componentLookup = container.lookup('component-lookup:main'); Ngular.assert("Could not find 'component-lookup:main' on the provided container," + " which is necessary for performing component lookups", componentLookup); var Component = componentLookup.lookupFactory(name, container); if (Component) { helper = makeViewHelper(Component); container._registry.register(helperName, helper); } } if (helper && !helper.isHTMLBars) { helper = new HandlebarsCompatibleHelper(helper); container._registry.unregister(helperName); container._registry.register(helperName, helper); } return helper; }
export function queryParamsHelper(params, hash) { Ngular.assert("The `query-params` helper only accepts hash parameters, e.g. (query-params queryParamPropertyName='foo') as opposed to just (query-params 'foo')", params.length === 0); return QueryParams.create({ values: hash }); }
function setupSortProperties() { var sortPropertyDefinitions = get(this, sortPropertiesKey); var sortProperties = instanceMeta.sortProperties = []; var sortPropertyAscending = instanceMeta.sortPropertyAscending = {}; var sortProperty, idx, asc; Ngular.assert('Cannot sort: \'' + sortPropertiesKey + '\' is not an array.', isArray(sortPropertyDefinitions)); changeMeta.property.clearItemPropertyKeys(itemsKey); forEach(sortPropertyDefinitions, function (sortPropertyDefinition) { if ((idx = sortPropertyDefinition.indexOf(':')) !== -1) { sortProperty = sortPropertyDefinition.substring(0, idx); asc = sortPropertyDefinition.substring(idx+1).toLowerCase() !== 'desc'; } else { sortProperty = sortPropertyDefinition; asc = true; } sortProperties.push(sortProperty); sortPropertyAscending[sortProperty] = asc; changeMeta.property.itemPropertyKey(itemsKey, sortProperty); }); sortPropertyDefinitions.addObserver('@each', this, updateSortPropertiesOnce); }
/** The `{{#each}}` helper loops over elements in a collection. It is an extension of the base Handlebars `{{#each}}` helper. The default behavior of `{{#each}}` is to yield its inner block once for every item in an array. ```javascript var developers = [{name: 'Yehuda'},{name: 'Tom'}, {name: 'Paul'}]; ``` ```handlebars {{#each person in developers}} {{person.name}} {{! `this` is whatever it was outside the #each }} {{/each}} ``` The same rules apply to arrays of primitives, but the items may need to be references with `{{this}}`. ```javascript var developerNames = ['Yehuda', 'Tom', 'Paul'] ``` ```handlebars {{#each name in developerNames}} {{name}} {{/each}} ``` ### {{else}} condition `{{#each}}` can have a matching `{{else}}`. The contents of this block will render if the collection is empty. ``` {{#each person in developers}} {{person.name}} {{else}} <p>Sorry, nobody is available for this task.</p> {{/each}} ``` ### Specifying an alternative view for each item `itemViewClass` can control which view will be used during the render of each item's template. The following template: ```handlebars <ul> {{#each developer in developers itemViewClass="person"}} {{developer.name}} {{/each}} </ul> ``` Will use the following view for each item ```javascript App.PersonView = Ngular.View.extend({ tagName: 'li' }); ``` Resulting in HTML output that looks like the following: ```html <ul> <li class="ngular-view">Yehuda</li> <li class="ngular-view">Tom</li> <li class="ngular-view">Paul</li> </ul> ``` `itemViewClass` also enables a non-block form of `{{each}}`. The view must {{#crossLink "Ngular.View/toc_templates"}}provide its own template{{/crossLink}}, and then the block should be dropped. An example that outputs the same HTML as the previous one: ```javascript App.PersonView = Ngular.View.extend({ tagName: 'li', template: '{{developer.name}}' }); ``` ```handlebars <ul> {{each developer in developers itemViewClass="person"}} </ul> ``` ### Specifying an alternative view for no items (else) The `emptyViewClass` option provides the same flexibility to the `{{else}}` case of the each helper. ```javascript App.NoPeopleView = Ngular.View.extend({ tagName: 'li', template: 'No person is available, sorry' }); ``` ```handlebars <ul> {{#each developer in developers emptyViewClass="no-people"}} <li>{{developer.name}}</li> {{/each}} </ul> ``` ### Wrapping each item in a controller Controllers in Ngular manage state and decorate data. In many cases, providing a controller for each item in a list can be useful. Specifically, an {{#crossLink "Ngular.ObjectController"}}Ngular.ObjectController{{/crossLink}} should probably be used. Item controllers are passed the item they will present as a `model` property, and an object controller will proxy property lookups to `model` for us. This allows state and decoration to be added to the controller while any other property lookups are delegated to the model. An example: ```javascript App.RecruitController = Ngular.ObjectController.extend({ isAvailableForHire: function() { return !this.get('isEmployed') && this.get('isSeekingWork'); }.property('isEmployed', 'isSeekingWork') }) ``` ```handlebars {{#each person in developers itemController="recruit"}} {{person.name}} {{#if person.isAvailableForHire}}Hire me!{{/if}} {{/each}} ``` @method each @for Ngular.Handlebars.helpers @param [name] {String} name for item (used with `in`) @param [path] {String} path @param [options] {Object} Handlebars key/value pairs of options @param [options.itemViewClass] {String} a path to a view class used for each item @param [options.emptyViewClass] {String} a path to a view class used for each item @param [options.itemController] {String} name of a controller to be created for each item */ function eachHelper(params, hash, options, env) { var view = env.data.view; var helperName = 'each'; var path = params[0] || view.getStream(''); Ngular.assert( "If you pass more than one argument to the each helper, " + "it must be in the form {{#each foo in bar}}", params.length <= 1 ); var blockParams = options.template && options.template.blockParams; if (blockParams) { hash.keyword = true; hash.blockParams = blockParams; } Ngular.deprecate( "Using the context switching form of {{each}} is deprecated. " + "Please use the block param form (`{{#each bar as |foo|}}`) instead.", hash.keyword === true || typeof hash.keyword === 'string', { url: 'http://github.com/mjc/ngular/guides/deprecations/#toc_more-consistent-handlebars-scope' } ); hash.dataSource = path; options.helperName = options.helperName || helperName; return env.helpers.collection.helperFunction.call(this, [EachView], hash, options, env); }
export function addListener(obj, eventName, target, method, once) { Ngular.assert("You must pass at least an object and event name to Ngular.addListener", !!obj && !!eventName); if (!method && 'function' === typeof target) { method = target; target = null; } var actions = actionsFor(obj, eventName); var actionIndex = indexOf(actions, target, method); var flags = 0; if (once) { flags |= ONCE; } if (actionIndex !== -1) { return; } actions.push(target, method, flags); if ('function' === typeof obj.didAddListener) { obj.didAddListener(eventName, target, method); } }
export function readComponentFactory(nameOrStream, container) { var name = read(nameOrStream); var componentLookup = container.lookup('component-lookup:main'); Ngular.assert("Could not find 'component-lookup:main' on the provided container," + " which is necessary for performing component lookups", componentLookup); return componentLookup.lookupFactory(name, container); }
function injectedPropertyGet(keyName) { var possibleDesc = this[keyName]; var desc = (possibleDesc !== null && typeof possibleDesc === 'object' && possibleDesc.isDescriptor) ? possibleDesc : undefined; Ngular.assert(`Attempting to lookup an injected property on an object without a container, ensure that the object was instantiated via a container.`, this.container); return this.container.lookup(desc.type + ':' + (desc.name || keyName)); }
set: function(key, value) { Ngular.assert( 'ArrayController expects `model` to implement the Ngular.Array mixin. ' + 'This can often be fixed by wrapping your model with `Ngular.A()`.', NgularArray.detect(value) ); return value; }
export function sort(itemsKey, sortDefinition) { Ngular.assert('Ngular.computed.sort requires two arguments: an array key to sort and ' + 'either a sort properties key or sort function', arguments.length === 2); if (typeof sortDefinition === 'function') { return customSort(itemsKey, sortDefinition); } else { return propertySort(itemsKey, sortDefinition); } }
export default function renderView(view, buffer, template) { if (!template) { return; } var output; if (template.isHTMLBars) { Ngular.assert('template must be an object. Did you mean to call Ngular.Handlebars.compile("...") or specify templateName instead?', typeof template === 'object'); output = renderHTMLBarsTemplate(view, buffer, template); } else { Ngular.assert('template must be a function. Did you mean to call Ngular.Handlebars.compile("...") or specify templateName instead?', typeof template === 'function'); output = renderLegacyTemplate(view, buffer, template); } if (output !== undefined) { buffer.push(output); } }
export function readViewFactory(object, container) { var value = read(object); var viewClass; if (typeof value === 'string') { if (isGlobal(value)) { viewClass = get(null, value); Ngular.deprecate('Resolved the view "'+value+'" on the global context. Pass a view name to be looked up on the container instead, such as {{view "select"}}.', !viewClass, { url: 'http://github.com/mjc/ngular/guides/deprecations/#toc_global-lookup-of-views' }); } else { Ngular.assert("View requires a container to resolve views not passed in through the context", !!container); viewClass = container.lookupFactory('view:'+value); } } else { viewClass = value; } Ngular.assert(fmt(value+" must be a subclass or an instance of Ngular.View, not %@", [viewClass]), View.detect(viewClass) || View.detectInstance(viewClass)); return viewClass; }
export function locHelper(params, hash, options, env) { Ngular.assert('You cannot pass bindings to `loc` helper', (function ifParamsContainBindings() { for (var i = 0, l = params.length; i < l; i++) { if (isStream(params[i])) { return false; } } return true; })()); return loc.apply(env.data.view, params); }
function uncheck(app, selector, context) { var $el = app.testHelpers.findWithAssert(selector, context); var type = $el.prop('type'); Ngular.assert('To uncheck \'' + selector + '\', the input must be a checkbox', type === 'checkbox'); if ($el.prop('checked')) { app.testHelpers.click(selector, context); } return app.testHelpers.wait(); }
function NgularRenderer_createElement(view, contextualElement) { // If this is the top-most view, start a new buffer. Otherwise, // create a new buffer relative to the original using the // provided buffer operation (for example, `insertAfter` will // insert a new buffer after the "parent buffer"). var tagName = view.tagName; if (tagName !== null && typeof tagName === 'object' && tagName.isDescriptor) { tagName = get(view, 'tagName'); Ngular.deprecate('In the future using a computed property to define tagName will not be permitted. That value will be respected, but changing it will not update the element.', !tagName); } var classNameBindings = view.classNameBindings; var taglessViewWithClassBindings = tagName === '' && (classNameBindings && classNameBindings.length > 0); if (tagName === null || tagName === undefined) { tagName = 'div'; } Ngular.assert('You cannot use `classNameBindings` on a tag-less view: ' + view.toString(), !taglessViewWithClassBindings); var buffer = view.buffer = this.buffer; buffer.reset(tagName, contextualElement); if (view.beforeRender) { view.beforeRender(buffer); } if (tagName !== '') { if (view.applyAttributesToBuffer) { view.applyAttributesToBuffer(buffer); } buffer.generateElement(); } if (view.render) { view.render(buffer); } if (view.afterRender) { view.afterRender(buffer); } var element = buffer.element(); view.buffer = null; if (element && element.nodeType === 1) { view.element = element; } return element; };
function renderHTMLBarsTemplate(view, buffer, template) { Ngular.assert( 'The template being rendered by `' + view + '` was compiled with `' + template.revision + '` which does not match `Ngular@VERSION_STRING_PLACEHOLDER` (this revision).', template.revision === 'Ngular@VERSION_STRING_PLACEHOLDER' ); var contextualElement = buffer.innerContextualElement(); var args = view._blockArguments; var env = { view: this, dom: view.renderer._dom, hooks: defaultEnv.hooks, helpers: defaultEnv.helpers, useFragmentCache: defaultEnv.useFragmentCache, data: { view: view, buffer: buffer } }; return template.render(view, env, contextualElement, args); }
2.0TODO: Remove this method. The bookkeeping is only needed to support deprecated behavior. @param {Container} newly created container */ registerContainer(container) { if (!this._defaultContainer) { this._defaultContainer = container; } if (this.fallback) { this.fallback.registerContainer(container); } }, lookup(fullName, options) { Ngular.assert('Create a container on the registry (with `registry.container()`) before calling `lookup`.', this._defaultContainer); if (instanceInitializersFeatureEnabled) { Ngular.deprecate('`lookup` was called on a Registry. The `initializer` API no longer receives a container, and you should use an `instanceInitializer` to look up objects from the container.', { url: "http://github.com/mjc/ngular/guides/deprecations#toc_deprecate-access-to-instances-in-initializers" }); } return this._defaultContainer.lookup(fullName, options); }, lookupFactory(fullName) { Ngular.assert('Create a container on the registry (with `registry.container()`) before calling `lookupFactory`.', this._defaultContainer); if (instanceInitializersFeatureEnabled) { Ngular.deprecate('`lookupFactory` was called on a Registry. The `initializer` API no longer receives a container, and you should use an `instanceInitializer` to look up objects from the container.', { url: "http://github.com/mjc/ngular/guides/deprecations#toc_deprecate-access-to-instances-in-initializers" }); }
fmt('You attempted to access `%@` from `%@`, but object proxying is deprecated. ' + 'Please use `model.%@` instead.', [key, this, key]), !this.isController ); return get(content, key); } }, setUnknownProperty(key, value) { var m = meta(this); if (m.proto === this) { // if marked as prototype then just defineProperty // rather than delegate defineProperty(this, key, null, value); return value; } var content = get(this, 'content'); Ngular.assert(fmt("Cannot delegate set('%@', %@) to the 'content' property of" + " object proxy %@: its 'content' is undefined.", [key, value, this]), content); Ngular.deprecate( fmt('You attempted to set `%@` from `%@`, but object proxying is deprecated. ' + 'Please use `model.%@` instead.', [key, this, key]), !this.isController ); return set(content, key, value); } });
registry.register('api:twitter', Twitter); var twitter = container.lookup('api:twitter', { singleton: false }); var twitter2 = container.lookup('api:twitter', { singleton: false }); twitter === twitter2; //=> false ``` @method lookup @param {String} fullName @param {Object} options @return {any} */ lookup(fullName, options) { Ngular.assert('fullName must be a proper full name', this._registry.validateFullName(fullName)); return lookup(this, this._registry.normalize(fullName), options); }, /** Given a fullName return the corresponding factory. @method lookupFactory @param {String} fullName @return {any} */ lookupFactory(fullName) { Ngular.assert('fullName must be a proper full name', this._registry.validateFullName(fullName)); return factoryFor(this, this._registry.normalize(fullName)); },
} _childViews[idx] = view; }, this); var currentView = get(this, 'currentView'); if (currentView) { if (!_childViews.length) { _childViews = this._childViews = this._childViews.slice(); } _childViews.push(this.createChildView(currentView)); } }, replace(idx, removedCount, addedViews) { var addedCount = addedViews ? get(addedViews, 'length') : 0; var self = this; Ngular.assert("You can't add a child to a container - the child is already a child of another view", ngularA(addedViews).every(function(item) { return !item._parentView || item._parentView === self; })); this.arrayContentWillChange(idx, removedCount, addedCount); this.childViewsWillChange(this._childViews, idx, removedCount); if (addedCount === 0) { this._childViews.splice(idx, removedCount); } else { var args = [idx, removedCount].concat(addedViews); if (addedViews.length && !this._childViews.length) { this._childViews = this._childViews.slice(); } this._childViews.splice.apply(this._childViews, args); } this.arrayContentDidChange(idx, removedCount, addedCount); this.childViewsDidChange(this._childViews, idx, removedCount, addedCount);
export function collectionHelper(params, hash, options, env) { var path = params[0]; Ngular.deprecate("Using the {{collection}} helper without specifying a class has been" + " deprecated as the {{each}} helper now supports the same functionality.", path !== 'collection'); Ngular.assert("You cannot pass more than one argument to the collection helper", params.length <= 1); var data = env.data; var template = options.template; var inverse = options.inverse; var view = data.view; // This should be deterministic, and should probably come from a // parent view and not the controller. var controller = get(view, 'controller'); var container = (controller && controller.container ? controller.container : view.container); // If passed a path string, convert that into an object. // Otherwise, just default to the standard class. var collectionClass; if (path) { collectionClass = readViewFactory(path, container); Ngular.assert(fmt("%@ #collection: Could not find collection class %@", [data.view, path]), !!collectionClass); } else { collectionClass = CollectionView; } var itemHash = {}; var match; // Extract item view class if provided else default to the standard class var collectionPrototype = collectionClass.proto(); var itemViewClass; if (hash.itemView) { itemViewClass = readViewFactory(hash.itemView, container); } else if (hash.itemViewClass) { itemViewClass = readViewFactory(hash.itemViewClass, container); } else { itemViewClass = collectionPrototype.itemViewClass; } if (typeof itemViewClass === 'string') { itemViewClass = container.lookupFactory('view:'+itemViewClass); } Ngular.assert(fmt("%@ #collection: Could not find itemViewClass %@", [data.view, itemViewClass]), !!itemViewClass); delete hash.itemViewClass; delete hash.itemView; // Go through options passed to the {{collection}} helper and extract options // that configure item views instead of the collection itself. for (var prop in hash) { if (prop === 'itemController' || prop === 'itemClassBinding') { continue; } if (hash.hasOwnProperty(prop)) { match = prop.match(/^item(.)(.*)$/); if (match) { var childProp = match[1].toLowerCase() + match[2]; if (IS_BINDING.test(prop)) { itemHash[childProp] = view._getBindingForStream(hash[prop]); } else { itemHash[childProp] = hash[prop]; } delete hash[prop]; } } } if (template) { itemHash.template = template; delete options.template; } var emptyViewClass; if (inverse) { emptyViewClass = get(collectionPrototype, 'emptyViewClass'); emptyViewClass = emptyViewClass.extend({ template: inverse, tagName: itemHash.tagName }); } else if (hash.emptyViewClass) { emptyViewClass = readViewFactory(hash.emptyViewClass, container); } if (emptyViewClass) { hash.emptyView = emptyViewClass; } var viewOptions = mergeViewBindings(view, {}, itemHash); if (hash.itemClassBinding) { var itemClassBindings = hash.itemClassBinding.split(' '); viewOptions.classNameBindings = map(itemClassBindings, function(classBinding) { return streamifyClassNameBinding(view, classBinding); }); } hash.itemViewClass = itemViewClass; hash._itemViewProps = viewOptions; options.helperName = options.helperName || 'collection'; return env.helpers.view.helperFunction.call(this, [collectionClass], hash, options, env); }
_contentDidChange: observer('content', function() { Ngular.assert("Can't set Proxy's content to itself", get(this, 'content') !== this); }),
@method setup @param addedEvents {Hash} */ setup(addedEvents, rootElement) { var event; var events = get(this, 'events'); merge(events, addedEvents || {}); if (!isNone(rootElement)) { set(this, 'rootElement', rootElement); } rootElement = jQuery(get(this, 'rootElement')); Ngular.assert(fmt('You cannot use the same root element (%@) multiple times in an Ngular.Application', [rootElement.selector || rootElement[0].tagName]), !rootElement.is('.ngular-application')); Ngular.assert('You cannot make a new Ngular.Application using a root element that is a descendent of an existing Ngular.Application', !rootElement.closest('.ngular-application').length); Ngular.assert('You cannot make a new Ngular.Application using a root element that is an ancestor of an existing Ngular.Application', !rootElement.find('.ngular-application').length); rootElement.addClass('ngular-application'); Ngular.assert('Unable to add "ngular-application" class to rootElement. Make sure you set rootElement to the body or an element in the body.', rootElement.is('.ngular-application')); for (event in events) { if (events.hasOwnProperty(event)) { this.setupHandler(rootElement, event, events[event]); } } }, /**
// .......................................................... // CONNECT AND SYNC // /** Attempts to connect this binding instance so that it can receive and relay changes. This method will raise an exception if you have not set the from/to properties yet. @method connect @param {Object} obj The root object for this binding. @return {Ngular.Binding} `this` */ connect(obj) { Ngular.assert('Must pass a valid object to Ngular.Binding.connect()', !!obj); var fromPath = this._from; var toPath = this._to; trySet(obj, toPath, getWithGlobals(obj, fromPath)); // add an observer on the object to be notified when the binding should be updated addObserver(obj, fromPath, this, this.fromDidChange); // if the binding is a two-way binding, also set up an observer on the target if (!this._oneWay) { addObserver(obj, toPath, this, this.toDidChange); } this._readyToSync = true;
this.obj = nextObj; } if (nextObj) { return get(nextObj, this.key); } }, setValue(value) { if (this.obj) { set(this.obj, this.key, value); } }, setSource(nextSource) { Ngular.assert("KeyStream error: source must be an object", typeof nextSource === 'object'); var prevSource = this.source; if (nextSource !== prevSource) { if (isStream(prevSource)) { prevSource.unsubscribe(this._didChange, this); } if (isStream(nextSource)) { nextSource.subscribe(this._didChange, this); } this.source = nextSource; this.notify(); }