Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Remove binding transforms

I'm willing to bring them back if there is a
very compelling use-case vs. computed properties.
  • Loading branch information...
commit 7ae011753e595086287f06733028b260e0526847 1 parent b9fcd86
Yehuda Katz wycats authored
3  .jshintrc
View
@@ -48,5 +48,6 @@
"undef": true,
"sub": true,
"strict": false,
- "white": false
+ "white": false,
+ "eqnull": true
}
599 packages/ember-metal/lib/binding.js
View
@@ -27,111 +27,18 @@ require('ember-metal/run_loop'); // Ember.run.schedule
*/
Ember.LOG_BINDINGS = false || !!Ember.ENV.LOG_BINDINGS;
-// ..........................................................
-// TYPE COERCION HELPERS
-//
-
-// Coerces a non-array value into an array.
-/** @private */
-function MULTIPLE(val) {
- if (val instanceof Array) return val;
- if (val === undefined || val === null) return [];
- return [val];
-}
-
-// Treats a single-element array as the element. Otherwise
-// returns a placeholder.
-/** @private */
-function SINGLE(val, placeholder) {
- if (val instanceof Array) {
- if (val.length>1) return placeholder;
- else return val[0];
- }
- return val;
-}
-
-// Coerces the binding value into a Boolean.
-
-var BOOL = {
- to: function (val) {
- return !!val;
- }
-};
-
-// Returns the Boolean inverse of the value.
-var NOT = {
- to: function NOT(val) {
- return !val;
- }
-};
-
var get = Ember.get,
getPath = Ember.getPath,
setPath = Ember.setPath,
guidFor = Ember.guidFor,
isGlobalPath = Ember.isGlobalPath;
-// Applies a binding's transformations against a value.
-/** @private */
-function getTransformedValue(binding, val, obj, dir) {
-
- // First run a type transform, if it exists, that changes the fundamental
- // type of the value. For example, some transforms convert an array to a
- // single object.
-
- var typeTransform = binding._typeTransform;
- if (typeTransform) { val = typeTransform(val, binding._placeholder); }
-
- // handle transforms
- var transforms = binding._transforms,
- len = transforms ? transforms.length : 0,
- idx;
-
- for(idx=0;idx<len;idx++) {
- var transform = transforms[idx][dir];
- if (transform) { val = transform.call(this, val, obj); }
- }
- return val;
-}
-
-/** @private */
-function empty(val) {
- return val===undefined || val===null || val==='' || (Ember.isArray(val) && get(val, 'length')===0) ;
-}
/** @private */
function getPathWithGlobals(obj, path) {
return getPath(isGlobalPath(path) ? window : obj, path);
}
-/** @private */
-function getTransformedFromValue(obj, binding) {
- var operation = binding._operation,
- fromValue;
- if (operation) {
- fromValue = operation(obj, binding._from, binding._operand);
- } else {
- fromValue = getPathWithGlobals(obj, binding._from);
- }
- return getTransformedValue(binding, fromValue, obj, 'to');
-}
-
-/** @private */
-function getTransformedToValue(obj, binding) {
- var toValue = getPath(obj, binding._to);
- return getTransformedValue(binding, toValue, obj, 'from');
-}
-
-/** @private */
-var AND_OPERATION = function(obj, left, right) {
- return getPathWithGlobals(obj, left) && getPathWithGlobals(obj, right);
-};
-
-/** @private */
-var OR_OPERATION = function(obj, left, right) {
- return getPathWithGlobals(obj, left) || getPathWithGlobals(obj, right);
-};
-
// ..........................................................
// BINDING
//
@@ -170,17 +77,6 @@ Binding.prototype = /** @scope Ember.Binding.prototype */ {
if (this._oneWay) {
copy._oneWay = true;
}
- if (this._transforms) {
- copy._transforms = this._transforms.slice(0);
- }
- if (this._typeTransform) {
- copy._typeTransform = this._typeTransform;
- copy._placeholder = this._placeholder;
- }
- if (this._operand) {
- copy._operand = this._operand;
- copy._operation = this._operation;
- }
return copy;
},
@@ -239,288 +135,6 @@ Binding.prototype = /** @scope Ember.Binding.prototype */ {
return this;
},
- /**
- Adds the specified transform to the array of transform functions for this Binding.
-
- A transform can be either a single function or an hash with `to` and `from` properties
- that are each transform functions. If a single function is provided it will be set as the `to` transform.
-
- Transform functions accept the value to be transformed as their first argument and
- and the object with the `-Binding` property as the second argument:
-
- Transform functions must return the transformed value.
-
- Namespace = Ember.Object.create({
- A: Ember.Object.create({
- aPropertyBinding: Ember.Binding.from("Namespace.B.bProperty").transform(function(value, object) {
- return ((Ember.typeOf(value) === 'number') && (value < 10)) ? 10 : value;
- })
- }),
- B: Ember.Object.create({})
- })
-
- Namespace.setPath('B.bProperty', 50)
- Namespace.getPath('A.aProperty') // 50
-
- Namespace.setPath('B.bProperty', 2)
- Namespace.getPath('A.aProperty') // 10, the minimum value
-
-
- Namespace = Ember.Object.create({
- A: Ember.Object.create({
- aPropertyBinding: Ember.Binding.from("Namespace.B.bProperty").transform({
- to: function(value){
- return value.toUpperCase();
- },
- from: function(value){
- return value.toLowerCase();
- }
- })
- }),
- B: Ember.Object.create({})
- })
-
- Namespace.setPath('B.bProperty', "Hello there")
- Namespace.getPath('B.bProperty') // "Hello there"
- Namespace.getPath('A.aProperty') // "HELLO THERE", toUpperCase'd in 'to' transform
-
- Namespace.setPath('A.aProperty', "GREETINGS")
- Namespace.getPath('A.aProperty') // "GREETINGS"
- Namespace.getPath('B.bProperty') // "greetings", toLowerCase'd in 'from' transform
-
-
- Transforms are invoked in the order they were added. If you are
- extending a binding and want to reset the transforms, you can call
- `resetTransform()` first.
-
- @param {Function|Object} transform the transform function or an object containing to/from functions
- @returns {Ember.Binding} this
- */
- transform: function(transform) {
- if ('function' === typeof transform) {
- transform = { to: transform };
- }
-
- if (!this._transforms) this._transforms = [];
- this._transforms.push(transform);
- return this;
- },
-
- /**
- Resets the transforms for the binding. After calling this method the
- binding will no longer transform values. You can then add new transforms
- as needed.
-
- @returns {Ember.Binding} this
- */
- resetTransforms: function() {
- this._transforms = null;
- return this;
- },
-
- /**
- Adds a transform to the Binding instance that will allow only single values to pass.
- If the value is an array, return values will be `undefined` for `[]`, the sole item in a
- single value array or the 'Multiple Placeholder' value for arrays with more than a single item.
-
- Namespace = Ember.Object.create({
- A: Ember.Object.create({
- aPropertyBinding: Ember.Binding.from("Namespace.B.bProperty").single()
- }),
- B: Ember.Object.create({})
- })
-
- Namespace.setPath('B.bProperty', 'a single value')
- Namespace.getPath('A.aProperty') // 'a single value'
-
- Namespace.setPath('B.bProperty', null)
- Namespace.getPath('A.aProperty') // null
-
- Namespace.setPath('B.bProperty', [])
- Namespace.getPath('A.aProperty') // undefined
-
- Namespace.setPath('B.bProperty', ['a single value'])
- Namespace.getPath('A.aProperty') // 'a single value'
-
- Namespace.setPath('B.bProperty', ['a value', 'another value'])
- Namespace.getPath('A.aProperty') // "@@MULT@@", the Multiple Placeholder
-
-
- You can pass in an optional multiple placeholder or the default will be used.
-
- That this transform will only happen on forward value. Reverse values are sent unchanged.
-
- @param {Object} [placeholder] Placeholder value.
- @returns {Ember.Binding} this
- */
- single: function(placeholder) {
- this._typeTransform = SINGLE;
- this._placeholder = placeholder || "@@MULT@@";
- return this;
- },
-
- /**
- Adds a transform to the Binding instance that will convert the passed value into an array. If
- the value is null or undefined, it will be converted to an empty array.
-
- Namespace = Ember.Object.create({
- A: Ember.Object.create({
- aPropertyBinding: Ember.Binding.from("Namespace.B.bProperty").multiple()
- }),
- B: Ember.Object.create({
- bProperty: 'an object'
- })
- })
-
- Namespace.getPath('A.aProperty') // ["an object"]
- Namespace.setPath('B.bProperty', null)
- Namespace.getPath('A.aProperty') // []
-
- @returns {Ember.Binding} this
- */
- multiple: function() {
- this._typeTransform = MULTIPLE;
- this._placeholder = null;
- return this;
- },
-
- /**
- Adds a transform to the Binding instance to convert the value to a bool value.
- The value will return `false` for: `false`, `null`, `undefined`, `0`, and an empty string,
- otherwise it will return `true`.
-
- Namespace = Ember.Object.create({
- A: Ember.Object.create({
- aPropertyBinding: Ember.Binding.from("Namespace.B.bProperty").bool()
- }),
- B: Ember.Object.create({
- bProperty: 'an object'
- })
- })
-
- Namespace.getPath('A.aProperty') // true
- Namespace.setPath('B.bProperty', false)
- Namespace.getPath('A.aProperty') // false
-
- @returns {Ember.Binding} this
- */
- bool: function() {
- this.transform(BOOL);
- return this;
- },
-
- /**
- Adds a transform to the Binding instance that will return the placeholder value
- if the value is null, undefined, an empty array or an empty string. See also notNull().
-
- Namespace = Ember.Object.create({
- A: Ember.Object.create({
- aPropertyBinding: Ember.Binding.from("Namespace.B.bProperty").notEmpty("Property was empty")
- }),
- B: Ember.Object.create({
- bProperty: []
- })
- })
-
- Namespace.getPath('A.aProperty') // "Property was empty"
- Namespace.setPath('B.bProperty', [1,2])
- Namespace.getPath('A.aProperty') // [1,2]
-
- @param {Object} [placeholder] Placeholder value.
- @returns {Ember.Binding} this
- */
- notEmpty: function(placeholder) {
Henning Kiel
hennk added a note

Hm, I really liked being able to write

nameBinding: Ember.Binding.from('inputName').notEmpty('(sin nombre)'),

instead of

name: function(){
  var inputName = this.get('inputName');
  if (Em.empty(inputName)){
    return '(no name)';
  } else {
    return inputName;
  }
}.property('inputName'),

Was there another reason to remove this apart from being able to remove nearly 1KLOC?

Yehuda Katz Owner
wycats added a note

You can do something like this:

Ember.transforms = {
  notEmpty: function(from, placeholder) {
    return Ember.computed(function(key) {
      var value = this.get(key);
      if (Ember.empty(value) {
        return placeholder;
      } else {
        return value;
      }
    }).property(from)
  }
}

name: Ember.transforms.notEmpty('inputName', '(sin nombre)');

I think we'll probably ship with a few of these. The main point was that transforms were duplicating the role of computed properties 99% of the time, with the added cost of having them in the binding system.

Henning Kiel
hennk added a note

Ah, that look's like a good replacement. Thanks for the answer :)

Trek Glowacki Owner
trek added a note

We should document this solution somewhere. I think the idea of "computed property macros" will be a good replacement for all the common case binding transform uses.

Just want to put in my 2 cents here. I think that the approach suggested here is not as robust of a solution in terms of chainability as the computed property route. I agree that binding transforms are a relatively advanced concept, but they provide a very useful niche of code that allows properties to be quickly edited. For example (forgive me for using SC-isms, I think for these purposes they're effectively equivalent),

MyApp.barController = SC.Object.create({
  myFooBinding: SC.Binding.from("MyApp.fooController.myFoo").oneWay().bool()not()
})

In addition, I think a good reason these exist in SproutCore is that it encouraged view scaffolds to look like object hashes. In my mind, function definitions are smells when constructing the structural bones of your app in SC. Ember eschews this in favor of templates, so possibly binding transforms make sense to remove because the bones are made in Handlebars.

Devin Torres
devinus added a note

@tim-evans "In addition, I think a good reason these exist in SproutCore is that it encouraged view scaffolds to look like object hashes. In my mind, function definitions are smells when constructing the structural bones of your app in SC. Ember eschews this in favor of templates, so possibly binding transforms make sense to remove because the bones are made in Handlebars."

You hit the nail on the head. Binding transforms don't really make sense in Ember anymore since our views aren't object hashes. Especially considering the performance of chained transforms, there really is no benefit to using them.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
- if (placeholder === null || placeholder === undefined) {
- placeholder = "@@EMPTY@@";
- }
-
- this.transform({
- to: function(val) { return empty(val) ? placeholder : val; }
- });
-
- return this;
- },
-
- /**
- Adds a transform to the Binding instance that returns the placeholder value
- if the value is null or undefined. Otherwise the value will passthrough untouched:
-
- Namespace = Ember.Object.create({
- A: Ember.Object.create({
- aPropertyBinding: Ember.Binding.from("Namespace.B.bProperty").notNull("Property was null")
- }),
- B: Ember.Object.create({})
- })
-
- Namespace.getPath('A.aProperty') // "Property was null"
- Namespace.setPath('B.bProperty', 'Some value')
- Namespace.getPath('A.aProperty') // 'Some value'
-
- @param {Object} [placeholder] Placeholder value.
- @returns {Ember.Binding} this
- */
- notNull: function(placeholder) {
- if (placeholder === null || placeholder === undefined) {
- placeholder = "@@EMPTY@@";
- }
-
- this.transform({
- to: function(val) { return (val === null || val === undefined) ? placeholder : val; }
- });
-
- return this;
- },
-
- /**
- Adds a transform to the Binding instance to convert the value to the inverse
- of a bool value. This uses the same transform as `bool` but inverts it:
- The value will return `true` for: `false`, `null`, `undefined`, `0`, and an empty string,
- otherwise it will return `false`
-
- Namespace = Ember.Object.create({
- A: Ember.Object.create({
- aPropertyBinding: Ember.Binding.from("Namespace.B.bProperty").not()
- }),
- B: Ember.Object.create({
- bProperty: false
- })
- })
-
- Namespace.getPath('A.aProperty') // true
- Namespace.setPath('B.bProperty', true)
- Namespace.getPath('A.aProperty') // false
-
- @returns {Ember.Binding} this
- */
- not: function() {
- this.transform(NOT);
- return this;
- },
-
- /**
- Adds a transform to the Binding instance that will return true if the
- value is null or undefined, false otherwise.
-
- Namespace = Ember.Object.create({
- A: Ember.Object.create({
- aPropertyBinding: Ember.Binding.from("Namespace.B.bProperty").isNull()
- }),
- B: Ember.Object.create({
- bProperty: null
- })
- })
-
- Namespace.getPath('A.aProperty') // true
- Namespace.setPath('B.bProperty', 'any value')
- Namespace.getPath('A.aProperty') // false
-
- @returns {Ember.Binding} this
- */
- isNull: function() {
- this.transform(function(val) { return val === null || val === undefined; });
- return this;
- },
-
/** @private */
toString: function() {
var oneWay = this._oneWay ? '[oneWay]' : '';
@@ -542,14 +156,11 @@ Binding.prototype = /** @scope Ember.Binding.prototype */ {
connect: function(obj) {
Ember.assert('Must pass a valid object to Ember.Binding.connect()', !!obj);
- var oneWay = this._oneWay, operand = this._operand;
+ var oneWay = this._oneWay;
// add an observer on the object to be notified when the binding should be updated
Ember.addObserver(obj, this._from, this, this.fromDidChange);
- // if there is an operand, add an observer onto it as well
- if (operand) { Ember.addObserver(obj, operand, this, this.fromDidChange); }
-
// if the binding is a two-way binding, also set up an observer on the target
// object.
if (!oneWay) { Ember.addObserver(obj, this._to, this, this.toDidChange); }
@@ -572,15 +183,12 @@ Binding.prototype = /** @scope Ember.Binding.prototype */ {
disconnect: function(obj) {
Ember.assert('Must pass a valid object to Ember.Binding.disconnect()', !!obj);
- var oneWay = this._oneWay, operand = this._operand;
+ var oneWay = this._oneWay;
// remove an observer on the object so we're no longer notified of
// changes that should update bindings.
Ember.removeObserver(obj, this._from, this, this.fromDidChange);
- // if there is an operand, remove the observer from it as well
- if (operand) Ember.removeObserver(obj, operand, this, this.fromDidChange);
-
// if the binding is two-way, remove the observer from the target as well
if (!oneWay) Ember.removeObserver(obj, this._to, this, this.toDidChange);
@@ -636,7 +244,7 @@ Binding.prototype = /** @scope Ember.Binding.prototype */ {
// if we're synchronizing from the remote object...
if (direction === 'fwd') {
- var fromValue = getTransformedFromValue(obj, this);
+ var fromValue = getPathWithGlobals(obj, this._from);
if (log) {
Ember.Logger.log(' ', this.toString(), '->', fromValue, obj);
}
@@ -649,7 +257,7 @@ Binding.prototype = /** @scope Ember.Binding.prototype */ {
}
// if we're synchronizing *to* the remote object
} else if (direction === 'back') {// && !this._oneWay) {
- var toValue = getTransformedToValue(obj, this);
+ var toValue = getPath(obj, this._to);
if (log) {
Ember.Logger.log(' ', this.toString(), '<-', toValue, obj);
}
@@ -691,201 +299,20 @@ mixinProperties(Binding,
/**
Creates a new Binding instance and makes it apply in a single direction.
- A one-way binding will relay changes on the "from" side object (supplies
- as the `from` argument) the "to" side, but not the other way around.
+ A one-way binding will relay changes on the "from" side object (supplies
+ as the `from` argument) the "to" side, but not the other way around.
This means that if you change the "to" side directly, the "from" side may have
a different value.
-
+
@param {String} from from path.
@param {Boolean} [flag] (Optional) passing nothing here will make the binding oneWay. You can
instead pass false to disable oneWay, making the binding two way again.
-
+
@see Ember.Binding.prototype.oneWay
*/
oneWay: function(from, flag) {
var C = this, binding = new C(null, from);
return binding.oneWay(flag);
- },
-
- /**
- Creates a new Binding instance, setting its `from` property the value
- of the first argument, and adds a `single` transform to its set of transforms.
-
- @param {String} from from path.
- @param {Object} [placeholder] Placeholder value.
-
- @see Ember.Binding.prototype.single
- */
- single: function(from, placeholder) {
- var C = this, binding = new C(null, from);
- return binding.single(placeholder);
- },
-
- /**
- Creates a new Binding instance, setting its `from` property the value
- of the first argument, and adds a `multiple` transform to its set of transforms.
-
- @param {String} from from path.
-
- @see Ember.Binding.prototype.multiple
- */
- multiple: function(from) {
- var C = this, binding = new C(null, from);
- return binding.multiple();
- },
-
- /**
- @see Ember.Binding.prototype.transform
- */
- transform: function(from, func) {
- if (!func) {
- func = from;
- from = null;
- }
- var C = this, binding = new C(null, from);
- return binding.transform(func);
- },
-
- /**
- Creates a new Binding instance, setting its `from` property the value
- of the first argument, and adds a `notEmpty` transform to its set of transforms.
-
- @param {String} from from path.
- @param {Object} [placeholder] Placeholder value.
- @see Ember.Binding.prototype.notEmpty
- */
- notEmpty: function(from, placeholder) {
- var C = this, binding = new C(null, from);
- return binding.notEmpty(placeholder);
- },
-
- /**
- Creates a new Binding instance, setting its `from` property the value
- of the first argument, and adds a `notNull` transform to its set of transforms.
-
- @param {String} from from path.
- @param {Object} [placeholder] Placeholder value.
- @see Ember.Binding.prototype.notNull
- */
- notNull: function(from, placeholder) {
- var C = this, binding = new C(null, from);
- return binding.notNull(placeholder);
- },
-
-
- /**
- Creates a new Binding instance, setting its `from` property the value
- of the first argument, and adds a `bool` transform to its set of transforms.
-
- @param {String} from from path.
- @see Ember.Binding.prototype.bool
- */
- bool: function(from) {
- var C = this, binding = new C(null, from);
- return binding.bool();
- },
-
- /**
- Creates a new Binding instance, setting its `from` property the value
- of the first argument, and adds a `not` transform to its set of transforms.
-
- @param {String} from from path.
- @see Ember.Binding.prototype.not
- */
- not: function(from) {
- var C = this, binding = new C(null, from);
- return binding.not();
- },
-
- /**
- Creates a new Binding instance, setting its `from` property the value
- of the first argument, and adds a `isNull` transform to its set of transforms.
-
- @param {String} from from path.
- @see Ember.Binding.prototype.isNull
- */
- isNull: function(from) {
- var C = this, binding = new C(null, from);
- return binding.isNull();
- },
-
- /**
- Creates a new Binding instance that forwards the logical 'AND' of values at 'pathA'
- and 'pathB' whenever either source changes.
-
- Note that the transform acts strictly as a one-way binding, working only in the direction
-
- 'pathA' AND 'pathB' --> value (value returned is the result of ('pathA' && 'pathB'))
-
- Usage example where a views's `isVisible` value is determined by
- whether something is selected in a list and whether the current user is
- allowed to delete:
-
- deleteButton: Ember.View.extend({
- isVisibleBinding: Ember.Binding.and('MyApp.itemsController.hasSelection', 'MyApp.userController.canDelete')
- })
-
- @param {String} pathA The first part of the conditional
- @param {String} pathB The second part of the conditional
- */
- and: function(pathA, pathB) {
- var C = this, binding = new C(null, pathA).oneWay();
- binding._operand = pathB;
- binding._operation = AND_OPERATION;
- return binding;
- },
-
- /**
- Creates a new Binding instance that forwards the 'OR' of values at 'pathA' and
- 'pathB' whenever either source changes. Note that the transform acts
- strictly as a one-way binding, working only in the direction
-
- 'pathA' AND 'pathB' --> value (value returned is the result of ('pathA' || 'pathB'))
-
- @param {String} pathA The first part of the conditional
- @param {String} pathB The second part of the conditional
- */
- or: function(pathA, pathB) {
- var C = this, binding = new C(null, pathA).oneWay();
- binding._operand = pathB;
- binding._operation = OR_OPERATION;
- return binding;
- },
-
- /**
- Registers a custom transform for use on any Binding:
-
- Ember.Binding.registerTransform('notLessThan', function(minValue) {
- return this.transform(function(value, binding) {
- return ((Ember.typeOf(value) === 'number') && (value < minValue)) ? minValue : value;
- });
- });
-
- Namespace = Ember.Object.create({
- A: Ember.Object.create({
- aPropertyBinding: Ember.Binding.from("Namespace.B.bProperty").notLessThan(10)
- }),
- B: Ember.Object.create({})
- })
-
- Namespace.setPath('B.bProperty', 50)
- Namespace.getPath('A.aProperty') // 50
-
- Namespace.setPath('B.bProperty', 2)
- Namespace.getPath('A.aProperty') // 10, the minimum value
-
- @param {String} name The name of the transform
- @param {Function} transform The transformation function
-
- @see Ember.Binding.prototype.transform
- */
- registerTransform: function(name, transform) {
- this.prototype[name] = transform;
- this[name] = function(from) {
- var C = this, binding = new C(null, from), args;
- args = Array.prototype.slice.call(arguments, 1);
- return binding[name].apply(binding, args);
- };
}
});
@@ -895,11 +322,11 @@ mixinProperties(Binding,
An Ember.Binding connects the properties of two objects so that whenever the
value of one property changes, the other property will be changed also.
-
+
## Automatic Creation of Bindings with `/^*Binding/`-named Properties
You do not usually create Binding objects directly but instead describe
bindings in your class or object definition using automatic binding detection.
-
+
Properties ending in a `Binding` suffix will be converted to Ember.Binding instances.
The value of this property should be a string representing a path to another object or
a custom binding instanced created using Binding helpers (see "Customizing Your Bindings"):
@@ -909,7 +336,7 @@ mixinProperties(Binding,
This will create a binding from `MyApp.someController.title` to the `value`
property of your object instance automatically. Now the two values will be
kept in sync.
-
+
## Customizing Your Bindings
In addition to synchronizing values, bindings can perform basic transforms on values.
@@ -934,7 +361,7 @@ mixinProperties(Binding,
and then check to see if the value is "empty" (null, undefined, empty array,
or an empty string). If it is empty, the value will be set to the string
"(EMPTY)".
-
+
The included transforms are: `and`, `bool`, `isNull`, `not`, `notEmpty`, `notNull`, `oneWay`,
`single`, and `multiple`
@@ -1063,7 +490,7 @@ mixinProperties(Binding,
Ember's built in binding creation method makes it easy to automatically
create bindings for you. You should always use the highest-level APIs
available, even if you understand how it works underneath.
-
+
@since Ember 0.9
*/
Ember.Binding = Binding;
58 packages/ember-metal/tests/binding/and_test.js
View
@@ -1,58 +0,0 @@
-// ==========================================================================
-// Project: Ember Runtime
-// Copyright: ©2011 Strobe Inc. and contributors.
-// License: Licensed under MIT license (see license.js)
-// ==========================================================================
-
-var MyApp, set = Ember.set, get = Ember.get;
-
-module('binding/and', {
- setup: function() {
- MyApp = {
- foo: false,
- bar: false
- };
-
- Ember.run(function(){
- Ember.Binding.and("foo", "bar").to("baz").connect(MyApp);
-
- });
- },
-
- teardown: function() {
- MyApp = null;
- }
-});
-
-test('should return second item when both are truthy', function() {
- Ember.run(function(){
- set(MyApp, 'foo', true);
- set(MyApp, 'bar', 'BAR');
- });
-
- equal(get(MyApp, 'baz'), 'BAR', 'should be false');
-});
-
-test('should return false first item', function() {
- Ember.run(function(){
- set(MyApp, 'foo', 0);
- set(MyApp, 'bar', true);
- });
- equal(get(MyApp, 'baz'), 0, 'should be false');
-});
-
-test('should return false second item', function() {
- Ember.run(function(){
- set(MyApp, 'foo', true);
- set(MyApp, 'bar', 0);
- });
- equal(get(MyApp, 'baz'), 0, 'should be false');
-});
-
-test('should return first item when both are false', function() {
- Ember.run(function(){
- set(MyApp, 'foo', 0);
- set(MyApp, 'bar', null);
- });
- equal(get(MyApp, 'baz'), 0, 'should be false');
-});
79 packages/ember-metal/tests/binding/bool_not_test.js
View
@@ -1,79 +0,0 @@
-// ==========================================================================
-// Project: Ember Runtime
-// Copyright: ©2011 Strobe Inc. and contributors.
-// License: Licensed under MIT license (see license.js)
-// ==========================================================================
-/*globals MyApp:true */
-
-function testBool(val, expected) {
- test('Forces '+Object.prototype.toString.call(val)+' value to '+expected, function() {
- Ember.run(function(){
- Ember.set(MyApp.foo, 'value', val);
- });
-
- equal(Ember.get(MyApp.bar, 'value'), expected);
- });
-}
-
-module('system/binding/bool', {
- setup: function() {
- MyApp = {
- foo: { value: 'FOO' },
- bar: { value: 'BAR' }
- };
-
- Ember.run(function(){
- Ember.bind(MyApp, 'bar.value', 'foo.value').bool();
- });
- },
-
- teardown: function() {
- MyApp = null;
- }
-});
-
-testBool(true, true);
-testBool('STRING', true);
-testBool(23, true);
-testBool({ object: 123 }, true);
-testBool([1,2,3], true);
-testBool([], true);
-
-testBool(false, false);
-testBool(null, false);
-testBool(undefined, false);
-testBool(0, false);
-testBool('', false);
-
-
-module('system/binding/not', {
- setup: function() {
- MyApp = {
- foo: { value: 'FOO' },
- bar: { value: 'BAR' }
- };
-
- Ember.run(function(){
- Ember.bind(MyApp, 'bar.value', 'foo.value').not();
- });
- },
-
- teardown: function() {
- MyApp = null;
- }
-});
-
-testBool(true, false);
-testBool('STRING', false);
-testBool(23, false);
-testBool({ object: 123 }, false);
-testBool([1,2,3], false);
-testBool([], false);
-
-testBool(false, true);
-testBool(null, true);
-testBool(undefined, true);
-testBool(0, true);
-testBool('', true);
-
-
49 packages/ember-metal/tests/binding/multiple_test.js
View
@@ -1,49 +0,0 @@
-// ==========================================================================
-// Project: Ember Runtime
-// Copyright: ©2011 Strobe Inc. and contributors.
-// License: Licensed under MIT license (see license.js)
-// ==========================================================================
-/*globals MyApp:true */
-
-module('system/binding/multiple', {
- setup: function() {
- MyApp = {
- foo: { value: 'FOO' },
- bar: { value: 'BAR' }
- };
- },
-
- teardown: function() {
- MyApp = null;
- }
-});
-
-test('forces binding values to be multiple', function() {
- var binding;
- Ember.run(function(){
- binding = Ember.bind(MyApp, 'bar.value', 'foo.value').multiple();
- });
-
- deepEqual(Ember.getPath('MyApp.bar.value'), ['FOO'], '1 MyApp.bar.value');
-
- Ember.run(function(){
- Ember.setPath('MyApp.foo.value', ['BAR']);
- });
-
- deepEqual(Ember.getPath('MyApp.foo.value'), ['BAR'], '2 MyApp.foo.value');
- deepEqual(Ember.getPath('MyApp.bar.value'), ['BAR'], '2 MyApp.bar.value');
-
- Ember.run(function(){
- Ember.setPath('MyApp.foo.value', ['BAR', 'BAZ']);
- });
-
- deepEqual(Ember.getPath('MyApp.foo.value'), ['BAR', 'BAZ'], '3 MyApp.foo.value');
- deepEqual(Ember.getPath('MyApp.bar.value'), ['BAR', 'BAZ'], '3 MyApp.bar.value');
-
- Ember.run(function(){
- Ember.setPath('MyApp.foo.value', null);
- });
-
- deepEqual(Ember.getPath('MyApp.bar.value'), [], '4 MyApp.bar.value');
-
-});
41 packages/ember-metal/tests/binding/notEmpty_test.js
View
@@ -1,41 +0,0 @@
-// ==========================================================================
-// Project: Ember Runtime
-// Copyright: ©2011 Strobe Inc. and contributors.
-// License: Licensed under MIT license (see license.js)
-// ==========================================================================
-/*globals MyApp:true */
-
-module('system/binding/notEmpty', {
- setup: function() {
- MyApp = {
- foo: { value: 'FOO' },
- bar: { value: 'BAR' }
- };
- },
-
- teardown: function() {
- MyApp = null;
- }
-});
-
-test('forces binding values to be notEmpty if enumerable', function() {
- var binding;
- Ember.run(function(){
- binding = Ember.bind(MyApp, 'bar.value', 'foo.value').notEmpty('(EMPTY)');
- });
-
- deepEqual(Ember.getPath('MyApp.bar.value'), 'FOO', '1 MyApp.bar.value');
-
- Ember.run(function(){
- Ember.setPath('MyApp.foo.value', ['FOO']);
- });
-
- deepEqual(Ember.getPath('MyApp.bar.value'), ['FOO'], '2 Array passes through');
-
- Ember.run(function(){
- Ember.setPath('MyApp.foo.value', []);
- });
-
- deepEqual(Ember.getPath('MyApp.bar.value'), '(EMPTY)', '3 uses empty placeholder');
-
-});
36 packages/ember-metal/tests/binding/notNull_test.js
View
@@ -1,36 +0,0 @@
-// ==========================================================================
-// Project: Ember Runtime
-// Copyright: ©2011 Strobe Inc. and contributors.
-// License: Licensed under MIT license (see license.js)
-// ==========================================================================
-/*globals MyApp:true */
-
-module('system/binding/notNull', {
- setup: function() {
- MyApp = {
- foo: { value: 'FOO' },
- bar: { value: 'BAR' }
- };
- },
-
- teardown: function() {
- MyApp = null;
- }
-});
-
-test('allow empty string as placeholder', function() {
- var binding;
- Ember.run(function(){
- binding = Ember.bind(MyApp, 'bar.value', 'foo.value').notNull('');
- });
-
- deepEqual(Ember.getPath('MyApp.bar.value'), 'FOO', 'value passes through');
-
- Ember.run(function(){
- Ember.setPath('MyApp.foo.value', null);
- });
-
- deepEqual(Ember.getPath('MyApp.bar.value'), '', 'null gets replaced');
-
-});
-
60 packages/ember-metal/tests/binding/or_test.js
View
@@ -1,60 +0,0 @@
-// ==========================================================================
-// Project: Ember Runtime
-// Copyright: ©2011 Strobe Inc. and contributors.
-// License: Licensed under MIT license (see license.js)
-// ==========================================================================
-
-var MyApp, set = Ember.set, get = Ember.get;
-
-module('binding/or', {
- setup: function() {
- MyApp = {
- foo: false,
- bar: false
- };
- Ember.run(function(){
- Ember.Binding.or("foo", "bar").to("baz").connect(MyApp);
- });
-
- },
-
- teardown: function() {
- MyApp = null;
- }
-});
-
-test('should return first item when both are truthy', function() {
- Ember.run(function(){
- set(MyApp, 'foo', 'FOO');
- set(MyApp, 'bar', 'BAR');
- });
-
- equal(get(MyApp, 'baz'), 'FOO', 'should be false');
-});
-
-test('should return true first item', function() {
- Ember.run(function(){
- set(MyApp, 'foo', 1);
- set(MyApp, 'bar', false);
- });
-
- equal(get(MyApp, 'baz'), 1, 'should be false');
-});
-
-test('should return true second item', function() {
- Ember.run(function(){
- set(MyApp, 'foo', false);
- set(MyApp, 'bar', 10);
- });
-
- equal(get(MyApp, 'baz'), 10, 'should be false');
-});
-
-test('should return second item when both are false', function() {
- Ember.run(function(){
- set(MyApp, 'foo', null);
- set(MyApp, 'bar', 0);
- });
-
- equal(get(MyApp, 'baz'), 0, 'should be false');
-});
65 packages/ember-metal/tests/binding/registerTransform_test.js
View
@@ -1,65 +0,0 @@
-// ==========================================================================
-// Project: Ember Metal
-// Copyright: ©2012 Strobe Inc. and contributors.
-// License: Licensed under MIT license (see license.js)
-// ==========================================================================
-/*globals MyApp:true */
-
-module('system/binding/registerTransform', {
- setup: function() {
- Ember.Binding.registerTransform('gt', function(min) {
- return this.transform(function(value, binding) {
- return value > min;
- });
- });
-
- MyApp = {
- foo: 0
- };
- },
-
- teardown: function() {
- delete Ember.Binding.gt;
- delete Ember.Binding.prototype.gt;
- MyApp = null;
- }
-});
-
-test('registerTransform registers a custom transform for use in a binding', function() {
- var binding;
-
- Ember.run(function(){
- Ember.setPath('MyApp.foo', 0);
-
- binding = Ember.bind(MyApp, 'bar', 'foo').gt(0);
- });
-
- equal(Ember.getPath('MyApp.bar'), false);
-
- Ember.run(function(){
- Ember.setPath('MyApp.foo', 1);
- });
-
- equal(Ember.getPath('MyApp.bar'), true);
-
- binding.disconnect(MyApp);
-});
-
-test('registerTransform adds a class method to Ember.Binding', function() {
- var binding;
-
- Ember.run(function(){
- Ember.setPath('MyApp.foo', 0);
- binding = Ember.Binding.gt('foo', 0).to('baz').connect(MyApp);
- });
-
- equal(Ember.getPath('MyApp.baz'), false);
-
- Ember.run(function(){
- Ember.setPath('MyApp.foo', 1);
- });
-
- equal(Ember.getPath('MyApp.baz'), true);
-
- binding.disconnect(MyApp);
-});
69 packages/ember-metal/tests/binding/single_test.js
View
@@ -1,69 +0,0 @@
-// Project: Ember Runtime
-// Copyright: ©2011 Strobe Inc. and contributors.
-// License: Licensed under MIT license (see license.js)
-// ==========================================================================
-/*globals MyApp:true */
-
-var get = Ember.get, set = Ember.set;
-
-module('system/binding/single', {
- setup: function() {
- MyApp = {
- foo: { value: 'FOO' },
- bar: { value: 'BAR' }
- };
- },
-
- teardown: function() {
- MyApp = null;
- }
-});
-
-test('forces binding values to be single', function() {
- var binding;
- Ember.run(function(){
- binding = Ember.bind(MyApp, 'bar.value', 'foo.value').single();
- });
-
-
- equal(Ember.getPath('MyApp.bar.value'), 'FOO', 'passes single object');
-
- Ember.run(function(){
- Ember.setPath('MyApp.foo.value', ['BAR']);
- });
-
- equal(Ember.getPath('MyApp.bar.value'), 'BAR', 'passes single object');
-
- Ember.run(function(){
- Ember.setPath('MyApp.foo.value', ['BAR', 'BAZ']);
- });
-
- equal(Ember.getPath('MyApp.bar.value'), "@@MULT@@", 'converts to placeholder');
-});
-
-test('Ember.Binding#single(fromPath, placeholder) is available', function() {
- var binding;
-
- var obj = {
- value: null,
- boundValue: null
- };
-
- Ember.run(function(){
- binding = Ember.Binding.single('value', 'placeholder').to('boundValue').connect(obj);
- });
-
- equal(get(obj, 'boundValue'), null, 'intial boundValue is null');
-
- Ember.run(function(){
- set(obj, 'value', [1]);
- });
-
- equal(get(obj, 'boundValue'), 1, 'passes single object');
-
- Ember.run(function(){
- set(obj, 'value', [1, 2]);
- });
-
- equal(get(obj, 'boundValue'), 'placeholder', 'converts to placeholder');
-});
46 packages/ember-metal/tests/binding/sync_test.js
View
@@ -130,52 +130,6 @@ testBoth("bindings should do the right thing when binding is in prototype", func
equal(get(obj, 'selection'), 'a');
});
-testBoth("binding with transform should only fire one change when set", function (get, set) {
- var a, b, changed, transform;
-
- Ember.run(function() {
- a = {array: null};
- b = {a: a};
- changed = 0;
-
- Ember.addObserver(a, 'array', function() {
- changed++;
- });
-
- transform = {
- to: function(array) {
- if (array) {
- return array.join(',');
- } else {
- return array;
- }
- },
- from: function(string) {
- if (string) {
- return string.split(',');
- } else {
- return string;
- }
- }
- };
- Ember.Binding.from('a.array').to('string').transform(transform).connect(b);
- });
-
- Ember.run(function() {
- set(a, 'array', ['a', 'b', 'c']);
- });
-
- equal(changed, 1);
- equal(get(b, 'string'), 'a,b,c');
-
- Ember.run(function() {
- set(b, 'string', '1,2,3');
- });
-
- equal(changed, 2);
- deepEqual(get(a, 'array'), ['1','2','3']);
-});
-
testBoth("bindings should not try to sync destroyed objects", function(get, set) {
var a, b;
157 packages/ember-metal/tests/binding/transform_test.js
View
@@ -1,157 +0,0 @@
-// ==========================================================================
-// Project: Ember Runtime
-// Copyright: ©2011 Strobe Inc. and contributors.
-// License: Licensed under MIT license (see license.js)
-// ==========================================================================
-/*globals MyApp:true */
-
-var foo, bar, binding, set = Ember.set, get = Ember.get, setPath = Ember.setPath;
-
-var CountObject = function(data){
- for (var item in data){
- this[item] = data[item];
- }
-
- Ember.addObserver(this, 'value', this.valueDidChange);
-};
-
-CountObject.prototype = {
- value: null,
-
- _count: 0,
-
- reset: function() {
- this._count = 0;
- return this;
- },
-
- valueDidChange: function() {
- this._count++;
- }
-};
-
-module('system/mixin/binding/transform_test', {
- setup: function() {
- MyApp = {
- foo: new CountObject({ value: 'FOO' }),
- bar: new CountObject({ value: 'BAR' })
- };
-
- foo = Ember.getPath('MyApp.foo');
- bar = Ember.getPath('MyApp.bar');
- },
-
- teardown: function() {
- binding.disconnect(MyApp);
- MyApp = null;
- }
-});
-
-test('returns this', function() {
- binding = new Ember.Binding('foo.value', 'bar.value');
-
- var ret = binding.transform({ from: function() {}, to: function() {} });
- equal(ret, binding);
-});
-
-test('transform function should be invoked on fwd change', function() {
-
- Ember.run(function(){
- binding = Ember.bind(MyApp, 'foo.value', 'bar.value');
- binding.transform({ to: function(value) { return 'TRANSFORMED'; }});
- Ember.run.sync();
- });
-
- // should have transformed...
- equal(Ember.getPath('MyApp.foo.value'), 'TRANSFORMED', 'should transform');
- equal(Ember.getPath('MyApp.bar.value'), 'BAR', 'should stay original');
-});
-
-test('two-way transforms work', function() {
- Ember.run(function() {
- binding = Ember.bind(MyApp, 'foo.value', 'bar.value');
- binding.transform({
- to: function(string) {
- return parseInt(string, 10) || null;
- },
- from: function(integer) {
- return String(integer);
- }
- });
- });
-
- Ember.run(function() {
- setPath(MyApp, 'bar.value', "1");
- });
-
- equal(Ember.getPath('MyApp.foo.value'), 1, "sets the value to a number");
-
- setPath(MyApp, 'foo.value', 1);
- equal(Ember.getPath('MyApp.bar.value'), "1", "sets the value to a string");
-});
-
-test('transform function should NOT be invoked on fwd change', function() {
-
- Ember.run(function(){
- var count = 0;
- binding = Ember.bind(MyApp, 'foo.value', 'bar.value');
- var lastSeenValue;
- binding.transform({
- to: function(value) {
- if (value !== lastSeenValue) count++; // transform must be consistent
- lastSeenValue = value;
- return 'TRANSFORMED '+count;
- }
- });
-
- Ember.run.sync();
-
- // should have transformed...
- foo.reset();
- bar.reset();
-
- Ember.setPath('MyApp.bar.value', 'FOOBAR');
- Ember.run.sync();
- });
-
-
- equal(Ember.getPath('MyApp.foo.value'), 'TRANSFORMED 2', 'should transform');
- equal(Ember.getPath('MyApp.bar.value'), 'FOOBAR', 'should stay original');
-
- equal(foo._count, 1, 'observer should have fired on set');
- equal(bar._count, 1, 'observer should have fired on set');
-});
-
-test('transforms should chain', function() {
- Ember.run(function () {
- binding = Ember.bind(MyApp, 'foo.value', 'bar.value');
- binding.transform({
- to: function(value) { return value+' T1'; }
- });
- binding.transform({
- to: function(value) { return value+' T2'; }
- });
- });
-
- // should have transformed...
- equal(Ember.getPath('MyApp.foo.value'), 'BAR T1 T2', 'should transform');
- equal(Ember.getPath('MyApp.bar.value'), 'BAR', 'should stay original');
-});
-
-test('resetTransforms() should clear', function() {
- Ember.run(function () {
- binding = Ember.bind(MyApp, 'foo.value', 'bar.value');
- binding.transform({
- to: function(value) { return value+' T1'; }
- });
- binding.resetTransforms();
- binding.transform({
- to: function(value) { return value+' T2'; }
- });
- });
-
- // should have transformed...
- equal(Ember.getPath('MyApp.foo.value'), 'BAR T2', 'should transform');
- equal(Ember.getPath('MyApp.bar.value'), 'BAR', 'should stay original');
-});
-
171 packages/ember-runtime/tests/legacy_1x/system/binding_test.js
View
@@ -231,38 +231,6 @@ module("Custom Binding", {
}
});
-test("Binding value1 such that it will recieve only single values", function() {
- var bon1 = Bon1.create({
- value1Binding: Ember.Binding.single("TestNamespace.bon2.val1"),
- array1Binding: Ember.Binding.single("TestNamespace.bon2.arr")
- });
- Ember.run.sync();
- var a = [23,31,12,21];
- set(bon2, "arr", a);
- set(bon2, "val1","changed");
- Ember.run.sync();
- equal(get(bon2, "val1"),get(bon1, "value1"));
- equal("@@MULT@@",get(bon1, "array1"));
-
- Ember.run.end();
-});
-
-test("Binding with transforms, function to check the type of value", function() {
- var jon = Bon1.create({
- value1Binding: Ember.Binding.transform({
- to: function(val1) {
- return (Ember.typeOf(val1) === 'string')? val1 : "";
- }
- }).from("TestNamespace.bon2.val1")
- });
- Ember.run.sync();
- set(bon2, "val1","changed");
- Ember.run.sync();
- equal(get(jon, "value1"), get(bon2, "val1"));
-
- Ember.run.end();
-});
-
test("two bindings to the same value should sync in the order they are initialized", function() {
Ember.run.begin();
@@ -297,145 +265,6 @@ test("two bindings to the same value should sync in the order they are initializ
});
// ..........................................................
-// AND BINDING
-//
-
-module("AND binding", {
-
- setup: function() {
- // temporarily set up two source objects in the Ember namespace so we can
- // use property paths to access them
- Ember.set(Ember, 'testControllerA', Ember.Object.create({ value: false }));
- Ember.set(Ember, 'testControllerB', Ember.Object.create({ value: false }));
-
- toObject = Ember.Object.create({
- value: null,
- valueBinding: Ember.Binding.and('Ember.testControllerA.value', 'Ember.testControllerB.value')
- });
- },
-
- teardown: function() {
- set(Ember, 'testControllerA', null);
- set(Ember, 'testControllerB', null);
- Ember.run.end();
- Ember.run.cancelTimers();
- }
-
-});
-
-test("toObject.value should be true if both sources are true", function() {
- Ember.run.begin();
- set(Ember.testControllerA, 'value', true);
- set(Ember.testControllerB, 'value', true);
- Ember.run.end();
-
- Ember.run.sync();
- equal(get(toObject, 'value'), true);
-});
-
-test("toObject.value should be false if either source is false", function() {
- Ember.run.begin();
- set(Ember.testControllerA, 'value', true);
- set(Ember.testControllerB, 'value', false);
- Ember.run.end();
-
- Ember.run.sync();
- equal(get(toObject, 'value'), false);
-
- Ember.run.begin();
- set(Ember.testControllerA, 'value', true);
- set(Ember.testControllerB, 'value', true);
- Ember.run.end();
-
- Ember.run.sync();
- equal(get(toObject, 'value'), true);
-
- Ember.run.begin();
- set(Ember.testControllerA, 'value', false);
- set(Ember.testControllerB, 'value', true);
- Ember.run.end();
-
- Ember.run.sync();
- equal(get(toObject, 'value'), false);
-});
-
-// ..........................................................
-// OR BINDING
-//
-
-module("OR binding", {
- setup: function() {
- // temporarily set up two source objects in the Ember namespace so we can
- // use property paths to access them
- Ember.set(Ember, 'testControllerA', Ember.Object.create({ value: false }));
- Ember.set(Ember, 'testControllerB', Ember.Object.create({ value: null }));
-
- toObject = Ember.Object.create({
- value: null,
- valueBinding: Ember.Binding.or('Ember.testControllerA.value', 'Ember.testControllerB.value')
- });
- },
-
- teardown: function() {
- set(Ember, 'testControllerA', null);
- set(Ember, 'testControllerB', null);
-
- Ember.run.end();
- Ember.run.cancelTimers();
- }
-
-});
-
-test("toObject.value should be first value if first value is truthy", function() {
- Ember.run.begin();
- set(Ember.testControllerA, 'value', 'first value');
- set(Ember.testControllerB, 'value', 'second value');
- Ember.run.end();
-
- Ember.run.sync();
- equal(get(toObject, 'value'), 'first value');
-});
-
-test("toObject.value should be second value if first is falsy", function() {
- Ember.run.begin();
- set(Ember.testControllerA, 'value', false);
- set(Ember.testControllerB, 'value', 'second value');
- Ember.run.end();
-
- Ember.run.sync();
- equal(get(toObject, 'value'), 'second value');
-});
-
-// ..........................................................
-// BINDING WITH []
-//
-
-module("Binding with '[]'", {
- setup: function() {
- fromObject = Ember.Object.create({ value: Ember.A() });
- toObject = Ember.Object.create({ value: '' });
- root = { toObject: toObject, fromObject: fromObject };
-
- binding = Ember.bind(root, 'toObject.value', 'fromObject.value.[]').transform(function(v) {
- return v ? v.join(',') : '';
- });
- },
-
- teardown: function() {
- root = fromObject = toObject = null;
- Ember.run.end();
- Ember.run.cancelTimers();
- }
-});
-
-test("Binding refreshes after a couple of items have been pushed in the array", function() {
- get(fromObject, 'value').pushObjects(['foo', 'bar']);
- Ember.run.sync();
- equal(get(toObject, 'value'), 'foo,bar');
-});
-
-
-// ..........................................................
// propertyNameBinding with longhand
//
40 packages/ember-runtime/tests/legacy_1x/system/object/bindings_test.js
View
@@ -84,26 +84,6 @@ test("bind(.bar) should bind to relative path", function() {
equal("changedValue", get(testObject, "foo"), "testObject.foo");
});
-test("Ember.Binding.bool(TestNamespace.fromObject.bar)) should create binding with bool transform", function() {
- Ember.run(function(){
- // create binding
- testObject.bind("foo", Ember.Binding.bool("TestNamespace.fromObject.bar"));
-
- // now make a change to see if the binding triggers.
- set(fromObject, "bar", 1);
- });
-
-
- equal(true, get(testObject, "foo"), "testObject.foo == true");
-
- Ember.run(function(){
- set(fromObject, "bar", 0);
- });
-
-
- equal(false, get(testObject, "foo"), "testObject.foo == false");
-});
-
var fooBindingModuleOpts = {
setup: function() {
@@ -165,26 +145,6 @@ test("fooBinding: .bar should bind to relative path", function() {
equal("changedValue", get(testObject, "foo"), "testObject.foo");
});
-test("fooBinding: Ember.Binding.bool(TestNamespace.fromObject.bar should create binding with bool transform", function() {
-
- Ember.run(function(){
- testObject = TestObject.create({
- fooBinding: Ember.Binding.bool("TestNamespace.fromObject.bar")
- });
-
- // now make a change to see if the binding triggers.
- set(fromObject, "bar", 1);
- });
-
- equal(true, get(testObject, "foo"), "testObject.foo == true");
-
- Ember.run(function(){
- set(fromObject, "bar", 0);
- });
-
- equal(false, get(testObject, "foo"), "testObject.foo == false");
-});
-
test('fooBinding: should disconnect bindings when destroyed', function () {
Ember.run(function(){
testObject = TestObject.create({

2 comments on commit 7ae0117

Henning Kiel

Hm, I really liked being able to write

nameBinding: Ember.Binding.from('inputName').notEmpty('(sin nombre)'),

instead of

name: function(){
  var inputName = this.get('inputName');
  if (Em.empty(inputName)){
    return '(no name)';
  } else {
    return inputName;
  }
}.property('inputName'),

Was there another reason to remove this apart from being able to remove nearly 1KLOC?

Yehuda Katz

You can do something like this:

Ember.transforms = {
  notEmpty: function(from, placeholder) {
    return Ember.computed(function(key) {
      var value = this.get(key);
      if (Ember.empty(value) {
        return placeholder;
      } else {
        return value;
      }
    }).property(from)
  }
}

name: Ember.transforms.notEmpty('inputName', '(sin nombre)');

I think we'll probably ship with a few of these. The main point was that transforms were duplicating the role of computed properties 99% of the time, with the added cost of having them in the binding system.

Henning Kiel

Ah, that look's like a good replacement. Thanks for the answer :)

Trek Glowacki

We should document this solution somewhere. I think the idea of "computed property macros" will be a good replacement for all the common case binding transform uses.

Tim Evans

Just want to put in my 2 cents here. I think that the approach suggested here is not as robust of a solution in terms of chainability as the computed property route. I agree that binding transforms are a relatively advanced concept, but they provide a very useful niche of code that allows properties to be quickly edited. For example (forgive me for using SC-isms, I think for these purposes they're effectively equivalent),

MyApp.barController = SC.Object.create({
  myFooBinding: SC.Binding.from("MyApp.fooController.myFoo").oneWay().bool()not()
})

In addition, I think a good reason these exist in SproutCore is that it encouraged view scaffolds to look like object hashes. In my mind, function definitions are smells when constructing the structural bones of your app in SC. Ember eschews this in favor of templates, so possibly binding transforms make sense to remove because the bones are made in Handlebars.

Devin Torres

@tim-evans "In addition, I think a good reason these exist in SproutCore is that it encouraged view scaffolds to look like object hashes. In my mind, function definitions are smells when constructing the structural bones of your app in SC. Ember eschews this in favor of templates, so possibly binding transforms make sense to remove because the bones are made in Handlebars."

You hit the nail on the head. Binding transforms don't really make sense in Ember anymore since our views aren't object hashes. Especially considering the performance of chained transforms, there really is no benefit to using them.

Lammertyn Pieter-Jan

Any reason why binding transforms is removed?

Trek Glowacki
Owner

there's some discussion on why in this very commit (but it's easy to miss given the size): 7ae0117#commitcomment-1408988

Please sign in to comment.
Something went wrong with that request. Please try again.