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

One-time binding doesn't work with filters #8605

Closed
cnshenj opened this issue Aug 13, 2014 · 22 comments
Closed

One-time binding doesn't work with filters #8605

cnshenj opened this issue Aug 13, 2014 · 22 comments

Comments

@cnshenj
Copy link

cnshenj commented Aug 13, 2014

This works fine:

<div ng-bind="test.value | number"></div>

This works fine too:

<div ng-bind="::test.value"></div>

But this renders an empty div:

<div ng-bind="::test.value | number"></div>
@jeffbcross jeffbcross self-assigned this Aug 13, 2014
@jeffbcross
Copy link
Contributor

I created a plnkr and can't reproduce the issue in Chrome.

What version of Angular are you using, and in what browser are you seeing this issue?

http://plnkr.co/edit/1kIw8FaQJQZLcvCArXS4?p=preview

@jeffbcross jeffbcross added this to the Purgatory milestone Aug 13, 2014
@jeffbcross jeffbcross removed their assignment Aug 13, 2014
@cnshenj
Copy link
Author

cnshenj commented Aug 13, 2014

plnkr is so slow.

To repro:

  1. Don't use ng-init, use ng-controller to assign the initial value: scope.test.value = undefined
  2. Assign the actual value outside of Angular and call $apply (e.g. do this when a button is clicked)

Without "::" the new value is properly processed by filter and bound to the element. Then add "::" to see the element become empty.

@caitp
Copy link
Contributor

caitp commented Aug 13, 2014

I'm pretty sure the reason for this is that the filter is not returning undefined when the input value is undefined. That seems like the expected behaviour.

@caitp
Copy link
Contributor

caitp commented Aug 13, 2014

Yep, numberFilter returns the empty string if the input value is null or undefined.

This doesn't really strike me as wrong per se, you could just write another filter that uses the number filter if the value is not undefined (the way one-time binding works is, it stops watching as soon as the value becomes something that is not undefined. But even if it worked the way the original prototype worked, where it watches until the value stabilizes, this would still not do what you want. Sorry!)

@cnshenj
Copy link
Author

cnshenj commented Aug 13, 2014

I believe Caitlin is right. However, rewriting all built-in filters to support one-time binding probably should be done in Angular, rather than be done by all users repeatedly.
If "a.b | filter" works, it's very natural to just add one-time binding attribute by prepending "::". Having two sets of almost identical filters to support non-one-time and one-time binding doesn't sound right.

@caitp
Copy link
Contributor

caitp commented Aug 13, 2014

There's no way for a filter to know if you are expecting the value to be one-time bound or not, unless we add a new flag or something, which is not likely to happen, and even if it did, you'd still have to tell the filter that's what was going on.

We could change the parser to not evaluate filters if the value is one-time bound and still undefined, but until we do that, this is really working as expected. That might be a complicated change :(

@cnshenj
Copy link
Author

cnshenj commented Aug 14, 2014

Since Angular expressions are forgiving, wouldn't it make sense for filters to return undefined instead of empty string when input is undefined or null? Thus, filters will work for both persistent and one-time bindings.

@IgorMinar
Copy link
Contributor

@caitp why not change the number filter to return undefined as @cnshenj suggested?

@IgorMinar IgorMinar modified the milestones: Purgatory, 1.3.0 Aug 21, 2014
@caitp
Copy link
Contributor

caitp commented Aug 21, 2014

I mean we could do that, but it would be a breaking change --- you'd get the same issue with all of the builtin filters though, if we get undefined in we'd have to return undefined out in order to make one-time binding happy.

I really think a better solution would be to make the parser somehow aware that it doesn't need to invoke filters if the input is undefined and it is using one-time binding, that would require less work in core and user-created filters

@IgorMinar IgorMinar modified the milestones: 1.3.0, 1.3.0-rc.0 Aug 25, 2014
@tbosch
Copy link
Contributor

tbosch commented Aug 27, 2014

Ok, here is an updated plunker: http://plnkr.co/edit/i6teCiKAe32c002IxrqV?p=preview

@caitp
Copy link
Contributor

caitp commented Aug 27, 2014

how about we say that all filters are pure (and stop associating state with them like ngLocale), and change the parser to not evaluate them when a value hasn't changed!

okay maybe that's unrealistic for 1.x :(

@tbosch
Copy link
Contributor

tbosch commented Aug 27, 2014

@caitp you are right, this would be the best option, but not feasible for 1.x

I vote against not invoking the filters in 1.x in the special case where the input is undefined and one-time binding is used. This would lead to issues that are hard to debug / find the cause of.

@IgorMinar
Copy link
Contributor

we could skip reevaluation of filters for primitive values, that would
solve the problem for one-time bindings and also work as perf improvement
for dirty-checking.

for objects/arrays we can't skip because we'd need to setup a deep watch
which might be more expensive than rerunning the filter.

On Wed, Aug 27, 2014 at 3:56 PM, Tobias Bosch [email protected]
wrote:

@caitp https://github.com/caitp you are right, this would be the best
option, but not feasible for 1.x

I vote against not invoking the filters in 1.x in the special case where
the input is undefined and one-time binding is used. This would lead to
issues that are hard to debug / find the cause of.


Reply to this email directly or view it on GitHub
#8605 (comment).

@rodyhaddad
Copy link
Contributor

What would we do if a filter isn't used at the end of the expression, but in the middle, like:

ng-change="callback({ key: (value | number) })"

@IgorMinar
Copy link
Contributor

that works by accident and not by design :)

On Thu, Aug 28, 2014 at 10:52 PM, Rodric Haddad [email protected]
wrote:

What would we do if a filter isn't used at the end of the expression, but
in the middle, like:

ng-change="callback({ key: (value | number) })"


Reply to this email directly or view it on GitHub
#8605 (comment).

@IgorMinar
Copy link
Contributor

I went through all the core filters and only number and currency filter are affected by this issue - both share a helper formatting fn that returns the empty string instead of undefined.

all other filters pass through the value if the value is not of the type that the filter is designed to handle.

IgorMinar added a commit to IgorMinar/angular.js that referenced this issue Aug 29, 2014
When these special values are passed through one-time binding will work correctly.

BREAKING CHANGE: previously the currency filter would convert null and undefined values into empty string, after this change
these values will be passed through.

Only cases when the currency filter is chained with another filter that doesn't expect null/undefined will be affected. This
should be very rare.

This change will not change the visual output of the filter because the interpolation will convert the null/undefined to
an empty string.

Closes angular#8605
IgorMinar added a commit to IgorMinar/angular.js that referenced this issue Aug 29, 2014
When these special values are passed through one-time binding will work correctly.

BREAKING CHANGE: previously the number filter would convert null and undefined values into empty string, after this change
these values will be passed through.

Only cases when the number filter is chained with another filter that doesn't expect null/undefined will be affected. This
should be very rare.

This change will not change the visual output of the filter because the interpolation will convert the null/undefined to
an empty string.

Closes angular#8605
IgorMinar added a commit to IgorMinar/angular.js that referenced this issue Aug 29, 2014
When these special values are passed through one-time binding will work correctly.

BREAKING CHANGE: previously the currency filter would convert null and undefined values into empty string, after this change
these values will be passed through.

Only cases when the currency filter is chained with another filter that doesn't expect null/undefined will be affected. This
should be very rare.

This change will not change the visual output of the filter because the interpolation will convert the null/undefined to
an empty string.

Closes angular#8605
IgorMinar added a commit to IgorMinar/angular.js that referenced this issue Aug 29, 2014
When these special values are passed through one-time binding will work correctly.

BREAKING CHANGE: previously the number filter would convert null and undefined values into empty string, after this change
these values will be passed through.

Only cases when the number filter is chained with another filter that doesn't expect null/undefined will be affected. This
should be very rare.

This change will not change the visual output of the filter because the interpolation will convert the null/undefined to
an empty string.

Closes angular#8605
IgorMinar added a commit that referenced this issue Aug 29, 2014
When these special values are passed through one-time binding will work correctly.

BREAKING CHANGE: previously the number filter would convert null and undefined values into empty string, after this change
these values will be passed through.

Only cases when the number filter is chained with another filter that doesn't expect null/undefined will be affected. This
should be very rare.

This change will not change the visual output of the filter because the interpolation will convert the null/undefined to
an empty string.

Closes #8605
Closes #8842
@stryju
Copy link

stryju commented Dec 16, 2014

I came across even weirder behavior...

when used filter that returns "random" output on one-time binding, it broke ALL the instances of one-time bindings in the scope

http://plnkr.co/edit/Cegf4S5ij3IQPhPOGhew?p=preview

@pkozlowski-opensource
Copy link
Member

@stryju one time binding or not a given expression needs to stabilize. Your filter makes it so the model never stabilizes hence the error.

@caitp
Copy link
Contributor

caitp commented Dec 16, 2014

Try something like this: http://plnkr.co/edit/QogrC0bOvX8EYnmQOhpf?p=preview

@stryju
Copy link

stryju commented Dec 16, 2014

@pkozlowski-opensource ah, good to know - thanks

@caitp 👍 thanks!

@Sojiro
Copy link

Sojiro commented Dec 6, 2017

@caitp I have different kind of issue with custom filters those are returning null during invalid inputs.
In these cases, the one-time binding works only if I return undefined and not null.

I am using AngularJS version - v1.5.8.

@seneyr
Copy link

seneyr commented Sep 5, 2018

@Sojiro I think that makes sense? Angular considers anything that's not undefined as a "stable" value: https://code.angularjs.org/1.5.8/docs/guide/expression#one-time-binding

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests