Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Support for bound handlebars helpers #615

Closed
wants to merge 1 commit into from

5 participants

@ghempton

This is my first attempt at adding a convenient way to create bound handlebars helpers. Bound handlebars helpers can now be created as follows:

  Ember.Handlebars.registerBoundHelper('capitalize', function(value) {
    return value.toUpperCase();
  });

Would love some feedback as to how this could be cleaned up.

@wagenet
Owner

This is based on the BoundProperty stuff I did right? If so, and if we merge this, should we change basic property bindings to use this as well?

@smadep

would this support bidirectional binding?!

@wagenet
Owner

@smadep No. If you want to bind to the DOM you have to do some extra work. As an example, the TextSupport mixin handle this for TextField and TextArea: https://github.com/emberjs/ember.js/blob/master/packages/ember-handlebars/lib/controls/text_support.js

@wagenet
Owner

@ghempton I think it would definitely be useful to get something like this in core. However, I know there was some concern from @wycats about the way rendering is handled here. @wycats, any more thoughts on the matter?

@trek
Owner

this isn't on the 1.0 milestone, but probably should be.

@nhoffmann

+1 for putting this on the 1.0 milestone

@wagenet
Owner

@nhoffmann Already there :)

@ghempton

I'd be happy to take another pass on this. What is the desired implementation? I think @wycats was wanting to reuse the code path from {{bind}}, but I'm wary of making Ember._HandlebarsBoundView more complex.

@ghempton ghempton referenced this pull request
Merged

Bound handlebars helpers #1274

@wagenet
Owner

Closing in favor of #1274,

@wagenet wagenet closed this
@knusul knusul referenced this pull request from a commit
Commit has since been removed from the repository and is no longer available.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Mar 24, 2012
  1. @ghempton
This page is out of date. Refresh to see the latest.
View
1  .gitignore
@@ -31,3 +31,4 @@ tmp
tmp*.gem
tmp.bpm
tmp.spade
+.project
View
28 packages/ember-handlebars/lib/ext.js
@@ -165,3 +165,31 @@ Ember.Handlebars.registerHelper('helperMissing', function(path, options) {
throw new Ember.Error(Ember.String.fmt(error, [view, path, this]));
});
+/**
+ Registers a bound helper in handlebars. This helper will automatically
+ update when the specified property path changes.
+
+ @param {String} name
+ @param {Function} func
+*/
+Ember.Handlebars.registerBoundHelper = function(name, func) {
+ var propertyPaths = Array.prototype.slice.call(arguments, 2);
+ Ember.Handlebars.registerHelper(name, function(property, options) {
+ var data = options.data,
+ view = data.view,
+ ctx = this;
+
+ var bindView = view.createChildView(Ember._BoundHelperView, {
+ property: property,
+ propertyPaths: propertyPaths,
+ context: ctx,
+ options: options.hash,
+ value: func
+ });
+
+ view.appendChild(bindView);
+ });
+};
+
+
+
View
1  packages/ember-handlebars/lib/views.js
@@ -5,4 +5,5 @@
// ==========================================================================
require("ember-handlebars/views/bindable_span");
+require("ember-handlebars/views/bound_helper_view");
require("ember-handlebars/views/metamorph_view");
View
51 packages/ember-handlebars/lib/views/bound_helper_view.js
@@ -0,0 +1,51 @@
+var get = Ember.get, set = Ember.set, getPath = Ember.Handlebars.getPath;
+
+require('ember-views/views/view');
+require('ember-handlebars/views/metamorph_view');
+
+Ember._BoundHelperView = Ember.View.extend(Ember.Metamorph, {
+
+ context: null,
+ options: null,
+ property: null,
+ // paths of the property that are also observed
+ propertyPaths: [],
+
+ value: Ember.K,
+
+ valueForRender: function() {
+ var value = this.value(Ember.getPath(this.context, this.property), this.options);
+ if (this.options.escaped) { value = Handlebars.Utils.escapeExpression(value); }
+ return value;
+ },
+
+ render: function(buffer) {
+ buffer.push(this.valueForRender());
+ },
+
+ valueDidChange: function() {
+ if (this.morph.isRemoved()) { return; }
+ this.morph.html(this.valueForRender());
+ },
+
+ didInsertElement: function() {
+ this.valueDidChange();
+ },
+
+ init: function() {
+ this._super();
+ Ember.addObserver(this.context, this.property, this, 'valueDidChange');
+ this.get('propertyPaths').forEach(function(propName) {
+ Ember.addObserver(this.context, this.property + '.' + propName, this, 'valueDidChange');
+ }, this);
+ },
+
+ destroy: function() {
+ Ember.removeObserver(this.context, this.property, this, 'valueDidChange');
+ this.get('propertyPaths').forEach(function(propName) {
+ this.context.removeObserver(this.property + '.' + propName, this, 'valueDidChange');
+ }, this);
+ this._super();
+ }
+
+});
View
63 packages/ember-handlebars/tests/handlebars_test.js
@@ -1709,4 +1709,67 @@ test("should bind to the property if no registered helper found for a mustache w
ok(view.$().text() === 'foobarProperty', "Property was bound to correctly");
});
+test("should update bound helpers when properties change", function() {
+ Ember.Handlebars.registerBoundHelper('capitalize', function(value) {
+ return value.toUpperCase();
+ });
+
+ view = Ember.View.create({
+ name: "Brogrammer",
+ template: Ember.Handlebars.compile("{{capitalize name}}")
+ });
+
+ appendView();
+
+ ok(view.$().text() === 'BROGRAMMER', "helper output is correct");
+
+ Ember.run(function() {
+ set(view, 'name', 'wes');
+ });
+
+ ok(view.$().text() === 'WES', "helper output updated");
+});
+
+test("should update bound helpers when sub-properties change", function() {
+ Ember.Handlebars.registerBoundHelper('capitalizeName', function(value) {
+ return get(value, 'name').toUpperCase();
+ }, 'name');
+
+ view = Ember.View.create({
+ person: Ember.Object.create({
+ name: 'Brogrammer'
+ }),
+ template: Ember.Handlebars.compile("{{capitalizeName person}}")
+ });
+
+ appendView();
+
+ ok(view.$().text() === 'BROGRAMMER', "helper output is correct");
+
+ Ember.run(function() {
+ set(view, 'person', Ember.Object.create({name: 'wes'}));
+ });
+
+ ok(view.$().text() === 'WES', "helper output updated");
+});
+
+test("bound helpers should support options", function() {
+ Ember.Handlebars.registerBoundHelper('repeat', function(value, options) {
+ var count = options.count;
+ var a = [];
+ while(a.length < count){
+ a.push(value);
+ }
+ return a.join('');
+ });
+
+ view = Ember.View.create({
+ text: 'ab',
+ template: Ember.Handlebars.compile("{{repeat text count=3}}")
+ });
+
+ appendView();
+
+ ok(view.$().text() === 'ababab', "helper output is correct");
+});
Something went wrong with that request. Please try again.