Skip to content

Instantly share code, notes, and snippets.

@wycats
Forked from anonymous/gist:2588583
Created May 3, 2012 19:35
Show Gist options
  • Save wycats/2588584 to your computer and use it in GitHub Desktop.
Save wycats/2588584 to your computer and use it in GitHub Desktop.
diff --git a/packages/ember-handlebars/lib/helpers/binding.js b/packages/ember-handlebars/lib/helpers/binding.js
index dd4fc0f..c0d28a7 100644
--- a/packages/ember-handlebars/lib/helpers/binding.js
+++ b/packages/ember-handlebars/lib/helpers/binding.js
@@ -13,141 +13,139 @@ var forEach = Ember.ArrayUtils.forEach;
var EmberHandlebars = Ember.Handlebars, helpers = EmberHandlebars.helpers;
-(function() {
- // Binds a property into the DOM. This will create a hook in DOM that the
- // KVO system will look for and update if the property changes.
- var bind = function(property, options, preserveContext, shouldDisplay, valueNormalizer) {
- var data = options.data,
- fn = options.fn,
- inverse = options.inverse,
- view = data.view,
- ctx = this,
- normalized;
-
- normalized = Ember.Handlebars.normalizePath(ctx, property, data);
-
- ctx = normalized.root;
- property = normalized.path;
-
- // Set up observers for observable objects
- if ('object' === typeof this) {
- // Create the view that will wrap the output of this template/property
- // and add it to the nearest view's childViews array.
- // See the documentation of Ember._HandlebarsBoundView for more.
- var bindView = view.createChildView(Ember._HandlebarsBoundView, {
- preserveContext: preserveContext,
- shouldDisplayFunc: shouldDisplay,
- valueNormalizerFunc: valueNormalizer,
- displayTemplate: fn,
- inverseTemplate: inverse,
- property: property,
- previousContext: ctx,
- isEscaped: options.hash.escaped,
- templateData: options.data
- });
-
- view.appendChild(bindView);
-
- /** @private */
- var observer = function() {
- Ember.run.once(bindView, 'rerenderIfNeeded');
- };
-
- // Observes the given property on the context and
- // tells the Ember._BindableSpan to re-render. If property
- // is an empty string, we are printing the current context
- // object ({{this}}) so updating it is not our responsibility.
- if (property !== '') {
- Ember.addObserver(ctx, property, observer);
- }
- } else {
- // The object is not observable, so just render it out and
- // be done with it.
- data.buffer.push(getPath(this, property, options));
- }
- };
+// Binds a property into the DOM. This will create a hook in DOM that the
+// KVO system will look for and update if the property changes.
+var bind = function(property, options, preserveContext, shouldDisplay, valueNormalizer) {
+ var data = options.data,
+ fn = options.fn,
+ inverse = options.inverse,
+ view = data.view,
+ ctx = this,
+ normalized;
+
+ normalized = Ember.Handlebars.normalizePath(ctx, property, data);
+
+ ctx = normalized.root;
+ property = normalized.path;
+
+ // Set up observers for observable objects
+ if ('object' === typeof this) {
+ // Create the view that will wrap the output of this template/property
+ // and add it to the nearest view's childViews array.
+ // See the documentation of Ember._HandlebarsBoundView for more.
+ var bindView = view.createChildView(Ember._HandlebarsBoundView, {
+ preserveContext: preserveContext,
+ shouldDisplayFunc: shouldDisplay,
+ valueNormalizerFunc: valueNormalizer,
+ displayTemplate: fn,
+ inverseTemplate: inverse,
+ property: property,
+ previousContext: ctx,
+ isEscaped: options.hash.escaped,
+ templateData: options.data
+ });
- /**
- '_triageMustache' is used internally select between a binding and helper for
- the given context. Until this point, it would be hard to determine if the
- mustache is a property reference or a regular helper reference. This triage
- helper resolves that.
-
- This would not be typically invoked by directly.
-
- @private
- @name Handlebars.helpers._triageMustache
- @param {String} property Property/helperID to triage
- @param {Function} fn Context to provide for rendering
- @returns {String} HTML string
- */
- EmberHandlebars.registerHelper('_triageMustache', function(property, fn) {
- Ember.assert("You cannot pass more than one argument to the _triageMustache helper", arguments.length <= 2);
- if (helpers[property]) {
- return helpers[property].call(this, fn);
- }
- else {
- return helpers.bind.apply(this, arguments);
+ view.appendChild(bindView);
+
+ /** @private */
+ var observer = function() {
+ Ember.run.once(bindView, 'rerenderIfNeeded');
+ };
+
+ // Observes the given property on the context and
+ // tells the Ember._BindableSpan to re-render. If property
+ // is an empty string, we are printing the current context
+ // object ({{this}}) so updating it is not our responsibility.
+ if (property !== '') {
+ Ember.addObserver(ctx, property, observer);
}
- });
+ } else {
+ // The object is not observable, so just render it out and
+ // be done with it.
+ data.buffer.push(getPath(this, property, options));
+ }
+};
- /**
- `bind` can be used to display a value, then update that value if it
- changes. For example, if you wanted to print the `title` property of
- `content`:
+/**
+ '_triageMustache' is used internally select between a binding and helper for
+ the given context. Until this point, it would be hard to determine if the
+ mustache is a property reference or a regular helper reference. This triage
+ helper resolves that.
- {{bind "content.title"}}
+ This would not be typically invoked by directly.
- This will return the `title` property as a string, then create a new
- observer at the specified path. If it changes, it will update the value in
- DOM. Note that if you need to support IE7 and IE8 you must modify the
- model objects properties using Ember.get() and Ember.set() for this to work as
- it relies on Ember's KVO system. For all other browsers this will be handled
- for you automatically.
+ @private
+ @name Handlebars.helpers._triageMustache
+ @param {String} property Property/helperID to triage
+ @param {Function} fn Context to provide for rendering
+ @returns {String} HTML string
+*/
+EmberHandlebars.registerHelper('_triageMustache', function(property, fn) {
+ Ember.assert("You cannot pass more than one argument to the _triageMustache helper", arguments.length <= 2);
+ if (helpers[property]) {
+ return helpers[property].call(this, fn);
+ }
+ else {
+ return helpers.bind.apply(this, arguments);
+ }
+});
- @private
- @name Handlebars.helpers.bind
- @param {String} property Property to bind
- @param {Function} fn Context to provide for rendering
- @returns {String} HTML string
- */
- EmberHandlebars.registerHelper('bind', function(property, fn) {
- Ember.assert("You cannot pass more than one argument to the bind helper", arguments.length <= 2);
+/**
+ `bind` can be used to display a value, then update that value if it
+ changes. For example, if you wanted to print the `title` property of
+ `content`:
+
+ {{bind "content.title"}}
+
+ This will return the `title` property as a string, then create a new
+ observer at the specified path. If it changes, it will update the value in
+ DOM. Note that if you need to support IE7 and IE8 you must modify the
+ model objects properties using Ember.get() and Ember.set() for this to work as
+ it relies on Ember's KVO system. For all other browsers this will be handled
+ for you automatically.
+
+ @private
+ @name Handlebars.helpers.bind
+ @param {String} property Property to bind
+ @param {Function} fn Context to provide for rendering
+ @returns {String} HTML string
+*/
+EmberHandlebars.registerHelper('bind', function(property, fn) {
+ Ember.assert("You cannot pass more than one argument to the bind helper", arguments.length <= 2);
- var context = (fn.contexts && fn.contexts[0]) || this;
+ var context = (fn.contexts && fn.contexts[0]) || this;
- return bind.call(context, property, fn, false, function(result) {
- return !Ember.none(result);
- });
+ return bind.call(context, property, fn, false, function(result) {
+ return !Ember.none(result);
});
+});
- /**
- Use the `boundIf` helper to create a conditional that re-evaluates
- whenever the bound value changes.
-
- {{#boundIf "content.shouldDisplayTitle"}}
- {{content.title}}
- {{/boundIf}}
-
- @private
- @name Handlebars.helpers.boundIf
- @param {String} property Property to bind
- @param {Function} fn Context to provide for rendering
- @returns {String} HTML string
- */
- EmberHandlebars.registerHelper('boundIf', function(property, fn) {
- var context = (fn.contexts && fn.contexts[0]) || this;
- var func = function(result) {
- if (Ember.typeOf(result) === 'array') {
- return get(result, 'length') !== 0;
- } else {
- return !!result;
- }
- };
+/**
+ Use the `boundIf` helper to create a conditional that re-evaluates
+ whenever the bound value changes.
- return bind.call(context, property, fn, true, func, func);
- });
-})();
+ {{#boundIf "content.shouldDisplayTitle"}}
+ {{content.title}}
+ {{/boundIf}}
+
+ @private
+ @name Handlebars.helpers.boundIf
+ @param {String} property Property to bind
+ @param {Function} fn Context to provide for rendering
+ @returns {String} HTML string
+*/
+EmberHandlebars.registerHelper('boundIf', function(property, fn) {
+ var context = (fn.contexts && fn.contexts[0]) || this;
+ var func = function(result) {
+ if (Ember.typeOf(result) === 'array') {
+ return get(result, 'length') !== 0;
+ } else {
+ return !!result;
+ }
+ };
+
+ return bind.call(context, property, fn, true, func, func);
+});
/**
@name Handlebars.helpers.with
@@ -156,10 +154,27 @@ var EmberHandlebars = Ember.Handlebars, helpers = EmberHandlebars.helpers;
@returns {String} HTML string
*/
EmberHandlebars.registerHelper('with', function(context, options) {
- 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);
+ if (arguments.length === 4) {
+ var keywordName, path;
+
+ 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];
+ preserveContext = true;
+ keywordName = arguments[2];
+ path = arguments[0];
- return helpers.bind.call(options.contexts[0], context, options);
+ Ember.assert("You must pass a block to the with helper", options.fn && options.fn !== Handlebars.VM.noop);
+
+ options.data.keywords[keywordName] = getPath(this, path);
+
+ return bind.call(this, path, options.fn, true, function(result) {
+ return !Ember.none(result);
+ });
+ } 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);
+ }
});
diff --git a/packages/ember-handlebars/tests/helpers/with_test.js b/packages/ember-handlebars/tests/helpers/with_test.js
new file mode 100644
index 0000000..cdf98f9
--- /dev/null
+++ b/packages/ember-handlebars/tests/helpers/with_test.js
@@ -0,0 +1,17 @@
+var appendView = function(view) {
+ Ember.run(function() { view.appendTo('#qunit-fixture'); });
+};
+
+module("Handlebars {{#with}} helper");
+
+test("it should support #with foo as bar", function() {
+ var view = Ember.View.create({
+ template: Ember.Handlebars.compile("{{#with person as tom}}{{title}}: {{tom.name}}{{/with}}"),
+ title: "Señor Engineer",
+ person: { name: "Tom Dale" }
+ });
+
+ appendView(view);
+
+ equal(view.$().text(), "Señor Engineer: Tom Dale", "should be properly scoped");
+});
diff --git a/packages/ember-views/lib/views/view.js b/packages/ember-views/lib/views/view.js
index 63ce13c..43b1b76 100644
--- a/packages/ember-views/lib/views/view.js
+++ b/packages/ember-views/lib/views/view.js
@@ -743,20 +743,23 @@ Ember.View = Ember.Object.extend(Ember.Evented,
templateData = this.get('templateData'),
controller = this.get('controller');
+ var keywords = templateData ? Ember.copy(templateData.keywords) : {};
+ keywords.view = get(this, 'concreteView');
+
+ // If the view has a controller specified, make it available to the
+ // template. If not, pass along the parent template's controller,
+ // if it exists.
+ if (controller) {
+ keywords.controller = controller;
+ }
+
var data = {
view: this,
buffer: buffer,
isRenderData: true,
- keywords: {
- view: get(this, 'concreteView')
- }
+ keywords: keywords
};
- // If the view has a controller specified, make it available to the
- // template. If not, pass along the parent template's controller,
- // if it exists.
- data.keywords.controller = controller || (templateData && templateData.keywords.controller);
-
// Invoke the template with the provided template context, which
// is the view by default. A hash of data is also passed that provides
// the template with access to the view and render buffer.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment