fix(ngBind): use same string representation as $interpolate

Fixes #11716

BREAKING CHANGE:

`ngBind` now uses the same logic as $interpolate (i.e. {{myString}}) when
binding, which means values other than strings are now transformed as following:
- null / undefined become empty string
- with an object's custom toString() function, except if the object is a Date, Array, or Number
- otherwise with JSON.stringify

Previously, ngBind would use always use toString().

The following examples show the different output:
```js
$scope.myPlainObject = {a: 1, b: 2};
$scope.myCustomObject = {a: 1, b: 2, toString: function() {return 'a+b';}};
```

Plain Object:
```html
<!-- Before: -->
<span ng-bind="myPlainObject">[object Object]</span>

<!-- After: -->
<span ng-bind="myPlainObject">{"a":1,"b":2}</span>
```

Object with custom toString():

```html
<!-- Before: -->
<span ng-bind="myCustomObject">[object Object]</span>

<!-- After: -->
<span ng-bind="myCustomObject">a+b</span>
```

If you want the output of `toString()`, you can use it directly on the value in ngBind:

```html
<span ng-bind="myObject.toString()">[object Object]</span>
```
This commit is contained in:
Martin Staffa
2016-06-05 14:52:18 +02:00
parent a5fd2e4c03
commit fa80a61a05
2 changed files with 38 additions and 1 deletions
+1 -1
View File
@@ -60,7 +60,7 @@ var ngBindDirective = ['$compile', function($compile) {
$compile.$$addBindingInfo(element, attr.ngBind);
element = element[0];
scope.$watch(attr.ngBind, function ngBindWatchAction(value) {
element.textContent = isUndefined(value) ? '' : value;
element.textContent = stringify(value);
});
};
}
+37
View File
@@ -46,6 +46,43 @@ describe('ngBind*', function() {
expect(element.text()).toEqual('-0false');
}));
they('should jsonify $prop', [[{a: 1}, '{"a":1}'], [true, 'true'], [false, 'false']], function(prop) {
inject(function($rootScope, $compile) {
$rootScope.value = prop[0];
element = $compile('<div ng-bind="value"></div>')($rootScope);
$rootScope.$digest();
expect(element.text()).toEqual(prop[1]);
});
});
it('should use custom toString when present', inject(function($rootScope, $compile) {
$rootScope.value = {
toString: function() {
return 'foo';
}
};
element = $compile('<div ng-bind="value"></div>')($rootScope);
$rootScope.$digest();
expect(element.text()).toEqual('foo');
}));
it('should NOT use toString on array objects', inject(function($rootScope, $compile) {
$rootScope.value = [];
element = $compile('<div ng-bind="value"></div>')($rootScope);
$rootScope.$digest();
expect(element.text()).toEqual('[]');
}));
it('should NOT use toString on Date objects', inject(function($rootScope, $compile) {
$rootScope.value = new Date(2014, 10, 10, 0, 0, 0);
element = $compile('<div ng-bind="value"></div>')($rootScope);
$rootScope.$digest();
expect(element.text()).toBe(JSON.stringify($rootScope.value));
expect(element.text()).not.toEqual($rootScope.value.toString());
}));
it('should one-time bind if the expression starts with two colons', inject(function($rootScope, $compile) {
element = $compile('<div ng-bind="::a"></div>')($rootScope);
$rootScope.a = 'lucas';