Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge pull request emberjs#4257 from rjackson/lazily-bound-attributeB…
…indings

Lazily bound attribute bindings
  • Loading branch information
stefanpenner committed Jan 31, 2014
2 parents a11d27d + fdfe849 commit 895ebcf
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 20 deletions.
2 changes: 1 addition & 1 deletion packages/ember-handlebars/lib/controls/text_area.js
Expand Up @@ -30,7 +30,7 @@ Ember.TextArea = Ember.Component.extend(Ember.TextSupport, {
classNames: ['ember-text-area'],

tagName: "textarea",
attributeBindings: ['rows', 'cols', 'name'],
attributeBindings: ['rows', 'cols', 'name', 'selectionEnd', 'selectionStart', 'wrap'],
rows: null,
cols: null,

Expand Down
6 changes: 5 additions & 1 deletion packages/ember-handlebars/lib/controls/text_field.js
Expand Up @@ -31,7 +31,11 @@ Ember.TextField = Ember.Component.extend(Ember.TextSupport, {

classNames: ['ember-text-field'],
tagName: "input",
attributeBindings: ['type', 'value', 'size', 'pattern', 'name', 'min', 'max'],
attributeBindings: ['type', 'value', 'size', 'pattern', 'name', 'min', 'max',
'accept', 'autocomplete', 'autosave', 'formaction',
'formenctype', 'formmethod', 'formnovalidate', 'formtarget',
'height', 'inputmode', 'list', 'multiple', 'pattern', 'step',
'width'],

/**
The `value` attribute of the input element. As the user inputs text, this
Expand Down
3 changes: 2 additions & 1 deletion packages/ember-handlebars/lib/controls/text_support.js
Expand Up @@ -20,7 +20,8 @@ var get = Ember.get, set = Ember.set;
Ember.TextSupport = Ember.Mixin.create(Ember.TargetActionSupport, {
value: "",

attributeBindings: ['placeholder', 'disabled', 'maxlength', 'tabindex', 'readonly'],
attributeBindings: ['placeholder', 'disabled', 'maxlength', 'tabindex', 'readonly',
'autofocus', 'form', 'selectionDirection', 'spellcheck', 'required'],
placeholder: null,
disabled: false,
maxlength: null,
Expand Down
74 changes: 57 additions & 17 deletions packages/ember-views/lib/views/view.js
Expand Up @@ -7,11 +7,12 @@ var states = {};
@submodule ember-views
*/

var get = Ember.get, set = Ember.set;
var guidFor = Ember.guidFor;
var a_forEach = Ember.EnumerableUtils.forEach;
var a_addObject = Ember.EnumerableUtils.addObject;
var meta = Ember.meta;
var get = Ember.get, set = Ember.set,
guidFor = Ember.guidFor,
a_forEach = Ember.EnumerableUtils.forEach,
a_addObject = Ember.EnumerableUtils.addObject,
meta = Ember.meta,
defineProperty = Ember.defineProperty;

var childViewsProperty = Ember.computed(function() {
var childViews = this._childViews, ret = Ember.A(), view = this;
Expand Down Expand Up @@ -1338,6 +1339,8 @@ Ember.View = Ember.CoreView.extend({
}, this);
},

_unspecifiedAttributeBindings: null,

/**
Iterates through the view's attribute bindings, sets up observers for each,
then applies the current value of the attributes to the passed render buffer.
Expand All @@ -1347,30 +1350,67 @@ Ember.View = Ember.CoreView.extend({
@private
*/
_applyAttributeBindings: function(buffer, attributeBindings) {
var attributeValue, elem;
var attributeValue,
unspecifiedAttributeBindings = this._unspecifiedAttributeBindings = this._unspecifiedAttributeBindings || {};

a_forEach(attributeBindings, function(binding) {
var split = binding.split(':'),
property = split[0],
attributeName = split[1] || property;

// Create an observer to add/remove/change the attribute if the
// JavaScript property changes.
var observer = function() {
elem = this.$();
if (property in this) {
this._setupAttributeBindingObservation(property, attributeName);

// Determine the current value and add it to the render buffer
// if necessary.
attributeValue = get(this, property);
Ember.View.applyAttributeBindings(buffer, attributeName, attributeValue);
} else {
unspecifiedAttributeBindings[property] = attributeName;
}
}, this);

Ember.View.applyAttributeBindings(elem, attributeName, attributeValue);
};
// Lazily setup setUnknownProperty after attributeBindings are initially applied
this.setUnknownProperty = this._setUnknownProperty;
},

_setupAttributeBindingObservation: function(property, attributeName) {
var attributeValue, elem;

this.registerObserver(this, property, observer);
// Create an observer to add/remove/change the attribute if the
// JavaScript property changes.
var observer = function() {
elem = this.$();

// Determine the current value and add it to the render buffer
// if necessary.
attributeValue = get(this, property);
Ember.View.applyAttributeBindings(buffer, attributeName, attributeValue);
}, this);

Ember.View.applyAttributeBindings(elem, attributeName, attributeValue);
};

this.registerObserver(this, property, observer);
},

/**
We're using setUnknownProperty as a hook to setup attributeBinding observers for
properties that aren't defined on a view at initialization time.
Note: setUnknownProperty will only be called once for each property.
@method setUnknownProperty
@param key
@param value
@private
*/
setUnknownProperty: null, // Gets defined after initialization by _applyAttributeBindings

_setUnknownProperty: function(key, value) {
var attributeName = this._unspecifiedAttributeBindings && this._unspecifiedAttributeBindings[key];
if (attributeName) {
this._setupAttributeBindingObservation(key, attributeName);
}

defineProperty(this, key);
return set(this, key, value);
},

/**
Expand Down

0 comments on commit 895ebcf

Please sign in to comment.