Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Inconsistency in ng-bind and interpolate #11716

Closed
just-boris opened this issue Apr 24, 2015 · 16 comments · Fixed by #14715
Closed

Inconsistency in ng-bind and interpolate #11716

just-boris opened this issue Apr 24, 2015 · 16 comments · Fixed by #14715

Comments

@just-boris
Copy link
Contributor

Here is example:
http://plnkr.co/edit/C95zazjXx5jfDrMqIE67?p=preview
Same data gives diffrerent output in interpolate and ng-bind

ng-bind: [object Object]
interpolate {"a":1,"b":2,"c":3}

I expected that ng-bind will render the same result that interpolate does.

@mhartington
Copy link

From my understanding, ng-bind is used internally for $interpolate to convert the data in to a string.

So in your example, ng-bind is pulling in the raw object, which is not converted to a string.
With {{data}} we're essentially saying bind to the raw data object and print it out a string.

Hope this helps a little bit

http://jsbin.com/jedete/5/edit?js,console,output

@gkalpak
Copy link
Member

gkalpak commented Apr 26, 2015

I am not really sure what @mhartington means, but I don't think ngBind is used by $interpolate. The difference is thatngBindassigns a value toelement.textValue(which implicitly converts the value to a string; and callingsomeObj.toString()returns[object Object]). $interpolate` on the other hand uses a custom "stringify" function that converts objects to JSON.

This merely confirms what OP already pointed out: that the behaviour of ngBind is different from that of $interpolate.
I am not sure why that is, but note that (a) $interpolate is not exclusively used in the view and (b) they shouldn't necessarily behave the same.

@just-boris
Copy link
Contributor Author

So, there is few thoughts:

  • Difference in behaviour should be noted in documentation. Now there is just said that "is similar but less verbose".
  • I need a directive version for interpolate behaviour. Now I can write a custom one, but why not to include it in the core.

@pkozlowski-opensource
Copy link
Member

@gkalpak would there be any reason for:

they shouldn't necessarily behave the same.

Honestly I'm not sure we've got this inconsistency and I can't think of any reason.

@gkalpak
Copy link
Member

gkalpak commented Apr 27, 2015

@pkozlowski-opensource: There might some reasons (for the nconsistency); but I wouldn't know.
I can't think of any either.

Changing $interpolate or text-interpolation would be too much of a breaking change though.
Chanigng ngBind to make it consistent with interpolation wouldn't hurt I guess (unless someone is relying on getting [object Object] back or passing very large objects to ngBind).

@pkozlowski-opensource
Copy link
Member

Right, I was thinking of eventually having ngBind following the logic of $interpolate

@lgalfaso
Copy link
Contributor

This looks like a valid issue and I have to agree that ngBind should follow the same logic that $interpolate has. Doing this without code duplication might turn to be a patch larger than expected, but given that it is a small breaking change, it would be ideal that if a fix lands, that it would be before we cut 1.4

@petebacondarwin WDYT?

@petebacondarwin
Copy link
Contributor

I agree that we should fix ngBind. I would prefer to prioritize fixing regressions for 1.4.0 before we make this change though. It is fairly low priority given that one can always work around it by writing a directive.
Also one can use ngBindTemplate too to get the same result.

@petebacondarwin
Copy link
Contributor

Actually I think this would be pretty straightforward. Just extract the stringify function from the $interpolate service factory and change the ngBind watch handler to:

        scope.$watch(attr.ngBind, function ngBindWatchAction(value) {
          element.textContent = stringify(value);
        });

@alexey-sh
Copy link

small crutch:

ng-bind="data|json"

@gkalpak
Copy link
Member

gkalpak commented Apr 28, 2015

@alexey-sh: The output is not exactly the same (this is more visible in elements that preserve whitespace; such as <pre>).
As @petebacondarwin already mentioned, ngBindTemplate="{{...}}" can be used (as a temporary workaround).

Updated plnkr

@TheCulliganMan
Copy link

Unsubscribe

On Tue, Apr 28, 2015, 5:27 PM Georgios Kalpakas [email protected]
wrote:

@alexey-sh https://github.com/alexey-sh: The output is not exactly the
same (this is more visible in elements that preserve whitespace; such as

).
As @petebacondarwin https://github.com/petebacondarwin already
mentioned, ngBindTemplate="{{...}}" can be used (as a temporary
workaround).

Updated plnkr http://plnkr.co/edit/3aSvYOsMzzXwHxuymfPR?p=preview

—
Reply to this email directly or view it on GitHub
https://github.com/angular/angular.js/issues/11716#issuecomment-97244590
.

kasperlewau referenced this issue in aeisenberg/angular-bind-notifier Jul 11, 2015
ng-bind works fine along with angular-bind-notifier, but when binding values other than primitives we are somewhat limited due to [https://github.com/angular/angular.js/issues/11716](https://github.com/angular/angular.js/issues/11716).
@Zehra-89
Copy link

Zehra-89 commented Sep 7, 2015

Hi please help me out, there is angular code, I want to verify h1 tag value.
I write protractor code= var ContactManager=element(by.binding('{::vm.oppDetails.managedBy.name}')).getText();
expect(ContactManager).toEqual('JamesE47');
but it says location binding not found.

angular js code:

{{::vm.oppDetails.managedBy.name}}

{{::vm.oppDetails.managedBy.title}}

Time: {{::vm.oppDetails.managedBy.officeHours}}

@FrailWords
Copy link

Hi All,

I came across an alternate problem that might be related to this? Please correct me if not.

While answering a question - http://stackoverflow.com/questions/34574448/filtering-some-data-with-two-kind-of-filter-in-angularjs-but-not-working/34576905, I replaced the {{ }} bindings with ng-bind in the results section of the plunkr in this post and the search result filtering started working fine. I was going by best practice and not exactly trying to figure out why this solved the problem.

I did take a look at ngBind.js code and was lead in the direction of $watch and $observe - one's on scope and the other's on the attribute. Is this something that affects how the values inside an ng-repeat show up and behave?

I felt like this is the best place for this question. Thank you.

@petebacondarwin
Copy link
Contributor

@FrailWords - I don't believe this is a problem with Angular. See my comment on SO.

@FrailWords
Copy link

@petebacondarwin thanks a lot for the correction.

@Narretz Narretz self-assigned this Jun 5, 2016
Narretz added a commit to Narretz/angular.js that referenced this issue Jun 5, 2016
Fixes angular#11716

BREAKING CHANGE:

`ngBind` now uses the same logic as $interpolate (i.e. {{myString}}) when
binding, which means objects are now transformed with JSON.stringify. Before, it
would use the object's toString() function.

The following example shows the different output:
```js
$scope.myObject = {a: 1, b: 2};
```

```html
<span ng-bind="myObject"></span>
```

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

After:
```html
<span ng-bind="myObject">{"a":1,"b":2}</span>
```
Narretz added a commit to Narretz/angular.js that referenced this issue Jun 5, 2016
Fixes angular#11716

BREAKING CHANGE:

`ngBind` now uses the same logic as $interpolate (i.e. {{myString}}) when
binding, which means objects are now transformed with JSON.stringify. Before, it
would use the object's toString() function.

The following example shows the different output:
```js
$scope.myObject = {a: 1, b: 2};
```

```html
<span ng-bind="myObject"></span>
```

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

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

Migration:
If you want the ouput of `toString()`, use a custom filter:
```js
angular.module('myApp').filter('toString', function() {
  return function toString(value) {
    return value.toString();
  };
});
```

```html
<span ng-bind="myObject | toString">[object Object]</span>
```
Narretz added a commit to Narretz/angular.js that referenced this issue Jun 6, 2016
Narretz added a commit to Narretz/angular.js that referenced this issue Jun 8, 2016
Fixes angular#11716

BREAKING CHANGE:

`ngBind` now uses the same logic as $interpolate (i.e. {{myString}}) when
binding, which means objects are now transformed to string either with an
object's custom toString() function, or if the object doesn't have one, 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>
```

Migration:
If you want the output of `toString()`, use a custom filter:
```js
angular.module('myApp').filter('toString', function() {
  return function toString(value) {
    return value.toString();
  };
});
```

```html
<span ng-bind="myObject | toString">[object Object]</span>
```
Narretz added a commit to Narretz/angular.js that referenced this issue Jun 8, 2016
Fixes angular#11716

BREAKING CHANGE:

`ngBind` now uses the same logic as $interpolate (i.e. {{myString}}) when
binding, which means objects are now transformed to string either with an
object's custom toString() function, or if the object doesn't have one, 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>
```

Migration:
If you want the output of `toString()`, use a custom filter:
```js
angular.module('myApp').filter('toString', function() {
  return function toString(value) {
    return value.toString();
  };
});
```

```html
<span ng-bind="myObject | toString">[object Object]</span>
```
Narretz added a commit to Narretz/angular.js that referenced this issue Jun 22, 2016
Fixes angular#11716

BREAKING CHANGE:

`ngBind` now uses the same logic as $interpolate (i.e. {{myString}}) when
binding, which means objects are now transformed to string with an
object's custom toString() function, except if the objects is a Date, Array, or Number, or
if the object doesn't have a custom toString(), 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>
```

Migration:
If you want the output of `toString()`, use a custom filter:
```js
angular.module('myApp').filter('toString', function() {
  return function toString(value) {
    return value.toString();
  };
});
```

```html
<span ng-bind="myObject | toString">[object Object]</span>
```
Narretz added a commit to Narretz/angular.js that referenced this issue Jun 22, 2016
Fixes angular#11716

BREAKING CHANGE:

`ngBind` now uses the same logic as $interpolate (i.e. {{myString}}) when
binding, which means objects are now transformed to string with an
object's custom toString() function, except if the objects is a Date, Array, or Number, or
if the object doesn't have a custom toString(), 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>
```

Migration:
If you want the output of `toString()`, use a custom filter:
```js
angular.module('myApp').filter('toString', function() {
  return function toString(value) {
    return value.toString();
  };
});
```

```html
<span ng-bind="myObject | toString">[object Object]</span>
```
Narretz added a commit to Narretz/angular.js that referenced this issue Jun 24, 2016
Fixes angular#11716

BREAKING CHANGE:

`ngBind` now uses the same logic as $interpolate (i.e. {{myString}}) when
binding, which means objects are now transformed to string with an
object's custom toString() function, except if the objects is a Date, Array, or Number, or
if the object doesn't have a custom toString(), 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>
```

Migration:
If you want the output of `toString()`, use a custom filter:
```js
angular.module('myApp').filter('toString', function() {
  return function toString(value) {
    return value.toString();
  };
});
```

```html
<span ng-bind="myObject | toString">[object Object]</span>
```
Narretz added a commit to Narretz/angular.js that referenced this issue Jun 24, 2016
Fixes angular#11716

BREAKING CHANGE:

`ngBind` now uses the same logic as $interpolate (i.e. {{myString}}) when
binding, which means objects are now transformed to string with an
object's custom toString() function, except if the objects is a Date, Array, or Number, or
if the object doesn't have a custom toString(), 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>
```

Migration:
If you want the output of `toString()`, use a custom filter:
```js
angular.module('myApp').filter('toString', function() {
  return function toString(value) {
    return value.toString();
  };
});
```

```html
<span ng-bind="myObject | toString">[object Object]</span>
```
Narretz added a commit to Narretz/angular.js that referenced this issue Jun 28, 2016
Fixes angular#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>
```
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.