run(function () { bind(root, 'toObject.value1', 'fromObject.value1'); bind(root, 'toObject.value2', 'fromObject.value2'); // change both value1 + value2, then flush bindings. observer should only // fire after bindings are done flushing. set(fromObject, 'value1', 'CHANGED'); set(fromObject, 'value2', 'CHANGED'); });
run(function() { a = { foo: 'trololol' }; b = { a: a }; bind(b, 'foo', 'a.foo'); c = { a: a }; bind(c, 'foo', 'a.foo'); });
run(function() { first = EmberObject.create({ output: 'first' }); second = EmberObject.extend({ inputDidChange: emberObserver('input', function() { set(this, 'output', get(this, 'input')); }) }).create({ input: 'second', output: 'second' }); third = EmberObject.create({ input: 'third' }); root = { first: first, second: second, third: third }; binding1 = bind(root, 'second.input', 'first.output'); binding2 = bind(root, 'second.output', 'third.input'); });
run(function() { first = EmberObject.create({ output: 'first' }); second = EmberObject.createWithMixins({ input: 'second', output: 'second', inputDidChange: emberObserver("input", function() { set(this, "output", get(this, "input")); }) }); third = EmberObject.create({ input: "third" }); root = { first: first, second: second, third: third }; binding1 = bind(root, 'second.input', 'first.output'); binding2 = bind(root, 'second.output', 'third.input'); });
init: function() { var controller; apply(this, this._super, arguments); var keywords = this.templateData.keywords; var keywordName = this.templateHash.keywordName; var keywordPath = this.templateHash.keywordPath; var controllerName = this.templateHash.controller; var preserveContext = this.preserveContext; if (controllerName) { var previousContext = this.previousContext; controller = this.container.lookupFactory('controller:'+controllerName).create({ parentController: previousContext, target: previousContext }); this._generatedController = controller; if (!preserveContext) { this.set('controller', controller); this.valueNormalizerFunc = function(result) { controller.set('model', result); return controller; }; } else { var controllerPath = jQuery.expando + guidFor(controller); keywords[controllerPath] = controller; emberBind(keywords, controllerPath + '.model', keywordPath); keywordPath = controllerPath; } } if (preserveContext) { emberBind(keywords, keywordName, keywordPath); } },
run(function() { a = {}; defineProperty(a, 'foo', computed(function() { getCalled++; if (getCalled > 1000) { throw 'infinite loop detected'; } return ['foo', 'bar']; })); b = { a: a }; bind(b, 'foo', 'a.foo'); });
run(function() { a = {}; defineProperty(a, 'foo', computed(function(key, value) { if (arguments.length === 2) { setCalled++; setValue = value; return value; } else { getCalled++; return setValue; } }).volatile()); b = { a: a }; bind(b, 'foo', 'a.foo'); });
run(function() { a = {}; defineProperty(a, 'foo', computed({ get: function(key) { getCalled++; return setValue; }, set: function(key, value) { setCalled++; setValue = value; return value; } }).volatile()); b = { a: a }; bind(b, 'foo', 'a.foo'); });
/** Use the `{{with}}` helper when you want to scope context. Take the following code as an example: ```handlebars <h5>{{user.name}}</h5> <div class="role"> <h6>{{user.role.label}}</h6> <span class="role-id">{{user.role.id}}</span> <p class="role-desc">{{user.role.description}}</p> </div> ``` `{{with}}` can be our best friend in these cases, instead of writing `user.role.*` over and over, we use `{{#with user.role}}`. Now the context within the `{{#with}} .. {{/with}}` block is `user.role` so you can do the following: ```handlebars <h5>{{user.name}}</h5> <div class="role"> {{#with user.role}} <h6>{{label}}</h6> <span class="role-id">{{id}}</span> <p class="role-desc">{{description}}</p> {{/with}} </div> ``` ### `as` operator This operator aliases the scope to a new name. It's helpful for semantic clarity and to retain default scope or to reference from another `{{with}}` block. ```handlebars // posts might not be {{#with user.posts as blogPosts}} <div class="notice"> There are {{blogPosts.length}} blog posts written by {{user.name}}. </div> {{#each post in blogPosts}} <li>{{post.title}}</li> {{/each}} {{/with}} ``` Without the `as` operator, it would be impossible to reference `user.name` in the example above. NOTE: The alias should not reuse a name from the bound property path. For example: `{{#with foo.bar as foo}}` is not supported because it attempts to alias using the first part of the property path, `foo`. Instead, use `{{#with foo.bar as baz}}`. ### `controller` option Adding `controller='something'` instructs the `{{with}}` helper to create and use an instance of the specified controller with the new context as its content. This is very similar to using an `itemController` option with the `{{each}}` helper. ```handlebars {{#with users.posts controller='userBlogPosts'}} {{!- The current context is wrapped in our controller instance }} {{/with}} ``` In the above example, the template provided to the `{{with}}` block is now wrapped in the `userBlogPost` controller, which provides a very elegant way to decorate the context with custom functions/properties. @method with @for Ember.Handlebars.helpers @param {Function} context @param {Hash} options @return {String} HTML string */ function withHelper(context, options) { if (arguments.length === 4) { var keywordName, path, rootPath, normalized, contextPath; Ember.assert("If you pass more than one argument to the with helper, it must be in the form #with foo as bar", arguments[1] === "as"); options = arguments[3]; keywordName = arguments[2]; path = arguments[0]; Ember.assert("You must pass a block to the with helper", options.fn && options.fn !== Handlebars.VM.noop); var localizedOptions = o_create(options); localizedOptions.data = o_create(options.data); localizedOptions.data.keywords = o_create(options.data.keywords || {}); if (isGlobalPath(path)) { contextPath = path; } else { normalized = normalizePath(this, path, options.data); path = normalized.path; rootPath = normalized.root; // This is a workaround for the fact that you cannot bind separate objects // together. When we implement that functionality, we should use it here. var contextKey = jQuery.expando + guidFor(rootPath); localizedOptions.data.keywords[contextKey] = rootPath; // if the path is '' ("this"), just bind directly to the current context contextPath = path ? contextKey + '.' + path : contextKey; } emberBind(localizedOptions.data.keywords, keywordName, contextPath); return bind.call(this, path, localizedOptions, true, exists); } else { Ember.assert("You must pass exactly one argument to the with helper", arguments.length === 2); Ember.assert("You must pass a block to the with helper", options.fn && options.fn !== Handlebars.VM.noop); return helpers.bind.call(options.contexts[0], context, options); } }
run(function() { obj = { selection: null }; selectionChanged = 0; addObserver(obj, 'selection', function () { selectionChanged++; }); proto = { obj: obj, changeSelection: function (value) { set(this, 'selection', value); } }; bind(proto, 'selection', 'obj.selection'); a = create(proto); b = create(proto); rewatch(a); rewatch(b); });
expectDeprecation(() => bind(b, 'foo', 'a.foo'), deprecationMessage);
expectDeprecation(() => { binding = bind(root, 'toObject.value', 'fromObject.value'); }, /`Ember\.Binding` is deprecated./);
expectDeprecation(() => { binding2 = bind(root, 'second.output', 'third.input'); }, /`Ember\.Binding` is deprecated./);
var A = function() { bind(this, 'foo', 'bar.baz'); };
expectDeprecation(() => bind(b, 'foo', 'a.foo'), /`Ember.Binding` is deprecated/);
run(function () { binding = bind(root, 'toObject.value', 'fromObject.value'); });
expectDeprecation(() => { bind(c, 'foo', 'a.foo'); }, deprecationMessage);
run(function() { bind(foo, 'value', 'bar.value'); });
expectDeprecation(() => { bind(foo, 'value', 'bar.value'); }, deprecationMessage);