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

multiple {{}} interpolations inside <option> are causing value created by ngValue to be replaced by <option>'s text #9842

Closed
fxck opened this issue Oct 30, 2014 · 4 comments

Comments

@fxck
Copy link

fxck commented Oct 30, 2014

See http://plnkr.co/edit/MXxLZ5wkVj4Cq2e5qQ4e?p=preview

works fine
    <select ng-model="baz.val">
      <option 
        ng-value="foo.val" 
        ng-repeat="foo in bar">
        {{ foo.name + ' ' + foo.second_name }}
      </option>
    </select> 
ng-value replaced in this case
    <select ng-model="baz.val">
      <option 
        ng-value="foo.val" 
        ng-repeat="foo in bar">
        {{ foo.name }} {{ foo.second_name }}
      </option>
    </select> 

also it's happening only if you use ng-value, it works fine when you use value="{{ val }}", see #9840

@fxck fxck changed the title multiple {{}} interpolations inside <option> are causing ngValue to be replaced by <option>'s text multiple {{}} interpolations inside <option> are causing value created by ngValue to be replaced by <option>'s text Oct 30, 2014
@Narretz
Copy link
Contributor

Narretz commented Oct 30, 2014

Funny, just today I stumbled upon https://code.angularjs.org/snapshot/docs/api/ng/directive/ngBindTemplate which is made for that exact purpose. I wonder if we should add this info to select documentation. Might get lost though, because it's crowed there.

@caitp
Copy link
Contributor

caitp commented Nov 11, 2014

Yes, lets get a docs fixup for this. PRs welcome.

@caitp
Copy link
Contributor

caitp commented Nov 11, 2014

(note: we are still not fixing the original bug --- but it would be good to update the documentation to be easier on people, as mentioned)

@Narretz Narretz self-assigned this Jun 6, 2015
@awerlang
Copy link

awerlang commented Aug 7, 2015

This behavior is explained by looking at https://github.com/angular/angular.js/blob/master/src/ng/rootScope.js#L445:

if (watchExpressions.length === 1) {
  // Special case size of one
  return this.$watch(watchExpressions[0], function watchGroupAction(value, oldValue, scope) {
      // omitted for brevity
  });
}

forEach(watchExpressions, function(expr, i) {
  var unwatchFn = self.$watch(expr, function watchGroupSubAction(value, oldValue) {
      // omitted for brevity
      self.$evalAsync(watchGroupAction);
  });
  deregisterFns.push(unwatchFn);
});

Whenever there's more that one expression to evaluate, because of $evalAsync() that'll be done some time later...later than assignment at option at least.

But...why does https://github.com/angular/angular.js/blob/master/src/ng/directive/select.js#L297 overwrites the value for option, when it already has one? I could trace this assignment back to 904b69c.

So, I believe a correction may be to verify if an option there's a value already.

@Narretz Narretz removed the PRs plz! label Sep 3, 2015
Narretz added a commit to Narretz/angular.js that referenced this issue Feb 6, 2016
select elements with ngModel will now set ngModel to option values added by ngValue.
This allows setting values of any type without the use of ngOptions.

Interpolations inside attributes can only be strings, but the ngValue directive uses attrs.$set,
which does not follow any type restriction. Any $observe on the value attribute will therefore receive
the original value (result of ngValue expression). However, when a user selects an option, the browser
sets the select value to the actual option's value attribute, which is still always a string.
For that reason, when option are added by ngValue, we set the hashed value of the original value in
the value attribute and store the actual value in an extra map. When the select value changes, we
read access the actual value via the hashed select value.

Closes angular#9842
Closes angular#6297
Narretz added a commit to Narretz/angular.js that referenced this issue May 17, 2016
select elements with ngModel will now set ngModel to option values added by ngValue.
This allows setting values of any type (not only strings) without the use of ngOptions.

Interpolations inside attributes can only be strings, but the ngValue directive uses attrs.$set,
which does not have any type restriction. Any $observe on the value attribute will therefore receive
the original value (result of ngValue expression). However, when a user selects an option, the browser
sets the select value to the actual option's value attribute, which is still always a string.
For that reason, when options are added by ngValue, we set the hashed value of the original value in
the value attribute and store the actual value in an extra map. When the select value changes, we
read access the actual value via the hashed select value.

Since we only use a hashed value for ngValue, we will have extra checks for the hashed values:
- when options are read, for both single and multiple select
- when options are written, for multiple select

I don't expect this to have a performance impact, but it should be kept in mind.

Closes angular#9842
Closes angular#6297

BREAKING CHANGE:

`<option>` elements added to `<select ng-model>` via `ngValue` now add their values in hash form, i.e.
`<option ng-value="myString">` becomes `<option ng-value="myString" value="string:myString">`.

This is done to support binding options with values of any type to selects.

This should rarely affect applications, as the values of options are usually not relevant to the
application logic, but it's possible that option values are checked in tests.
Narretz added a commit to Narretz/angular.js that referenced this issue May 17, 2016
select elements with ngModel will now set ngModel to option values added by ngValue.
This allows setting values of any type (not only strings) without the use of ngOptions.

Interpolations inside attributes can only be strings, but the ngValue directive uses attrs.$set,
which does not have any type restriction. Any $observe on the value attribute will therefore receive
the original value (result of ngValue expression). However, when a user selects an option, the browser
sets the select value to the actual option's value attribute, which is still always a string.
For that reason, when options are added by ngValue, we set the hashed value of the original value in
the value attribute and store the actual value in an extra map. When the select value changes, we
read access the actual value via the hashed select value.

Since we only use a hashed value for ngValue, we will have extra checks for the hashed values:
- when options are read, for both single and multiple select
- when options are written, for multiple select

I don't expect this to have a performance impact, but it should be kept in mind.

Closes angular#9842
Closes angular#6297

BREAKING CHANGE:

`<option>` elements added to `<select ng-model>` via `ngValue` now add their values in hash form, i.e.
`<option ng-value="myString">` becomes `<option ng-value="myString" value="string:myString">`.

This is done to support binding options with values of any type to selects.

This should rarely affect applications, as the values of options are usually not relevant to the
application logic, but it's possible that option values are checked in tests.
petebacondarwin pushed a commit to petebacondarwin/angular.js that referenced this issue May 30, 2016
select elements with ngModel will now set ngModel to option values added by ngValue.
This allows setting values of any type (not only strings) without the use of ngOptions.

Interpolations inside attributes can only be strings, but the ngValue directive uses attrs.$set,
which does not have any type restriction. Any $observe on the value attribute will therefore receive
the original value (result of ngValue expression). However, when a user selects an option, the browser
sets the select value to the actual option's value attribute, which is still always a string.
For that reason, when options are added by ngValue, we set the hashed value of the original value in
the value attribute and store the actual value in an extra map. When the select value changes, we
read access the actual value via the hashed select value.

Since we only use a hashed value for ngValue, we will have extra checks for the hashed values:
- when options are read, for both single and multiple select
- when options are written, for multiple select

I don't expect this to have a performance impact, but it should be kept in mind.

Closes angular#9842
Closes angular#6297

BREAKING CHANGE:

`<option>` elements added to `<select ng-model>` via `ngValue` now add their values in hash form, i.e.
`<option ng-value="myString">` becomes `<option ng-value="myString" value="string:myString">`.

This is done to support binding options with values of any type to selects.

This should rarely affect applications, as the values of options are usually not relevant to the
application logic, but it's possible that option values are checked in tests.
Narretz added a commit to Narretz/angular.js that referenced this issue Jun 1, 2016
select elements with ngModel will now set ngModel to option values added by ngValue.
This allows setting values of any type (not only strings) without the use of ngOptions.

Interpolations inside attributes can only be strings, but the ngValue directive uses attrs.$set,
which does not have any type restriction. Any $observe on the value attribute will therefore receive
the original value (result of ngValue expression). However, when a user selects an option, the browser
sets the select value to the actual option's value attribute, which is still always a string.
For that reason, when options are added by ngValue, we set the hashed value of the original value in
the value attribute and store the actual value in an extra map. When the select value changes, we
read access the actual value via the hashed select value.

Since we only use a hashed value for ngValue, we will have extra checks for the hashed values:
- when options are read, for both single and multiple select
- when options are written, for multiple select

I don't expect this to have a performance impact, but it should be kept in mind.

Closes angular#9842
Closes angular#6297

BREAKING CHANGE:

`<option>` elements added to `<select ng-model>` via `ngValue` now add their values in hash form, i.e.
`<option ng-value="myString">` becomes `<option ng-value="myString" value="string:myString">`.

This is done to support binding options with values of any type to selects.

This should rarely affect applications, as the values of options are usually not relevant to the
application logic, but it's possible that option values are checked in tests.
petebacondarwin pushed a commit to petebacondarwin/angular.js that referenced this issue Jun 17, 2016
select elements with ngModel will now set ngModel to option values added by ngValue.
This allows setting values of any type (not only strings) without the use of ngOptions.

Interpolations inside attributes can only be strings, but the ngValue directive uses attrs.$set,
which does not have any type restriction. Any $observe on the value attribute will therefore receive
the original value (result of ngValue expression). However, when a user selects an option, the browser
sets the select value to the actual option's value attribute, which is still always a string.
For that reason, when options are added by ngValue, we set the hashed value of the original value in
the value attribute and store the actual value in an extra map. When the select value changes, we
read access the actual value via the hashed select value.

Since we only use a hashed value for ngValue, we will have extra checks for the hashed values:
- when options are read, for both single and multiple select
- when options are written, for multiple select

I don't expect this to have a performance impact, but it should be kept in mind.

Closes angular#9842
Closes angular#6297

BREAKING CHANGE:

`<option>` elements added to `<select ng-model>` via `ngValue` now add their values in hash form, i.e.
`<option ng-value="myString">` becomes `<option ng-value="myString" value="string:myString">`.

This is done to support binding options with values of any type to selects.

This should rarely affect applications, as the values of options are usually not relevant to the
application logic, but it's possible that option values are checked in tests.
Narretz added a commit to Narretz/angular.js that referenced this issue Jun 20, 2016
select elements with ngModel will now set ngModel to option values added by ngValue.
This allows setting values of any type (not only strings) without the use of ngOptions.

Interpolations inside attributes can only be strings, but the ngValue directive uses attrs.$set,
which does not have any type restriction. Any $observe on the value attribute will therefore receive
the original value (result of ngValue expression). However, when a user selects an option, the browser
sets the select value to the actual option's value attribute, which is still always a string.
For that reason, when options are added by ngValue, we set the hashed value of the original value in
the value attribute and store the actual value in an extra map. When the select value changes, we
read access the actual value via the hashed select value.

Since we only use a hashed value for ngValue, we will have extra checks for the hashed values:
- when options are read, for both single and multiple select
- when options are written, for multiple select

I don't expect this to have a performance impact, but it should be kept in mind.

Closes angular#9842
Closes angular#6297

BREAKING CHANGE:

`<option>` elements added to `<select ng-model>` via `ngValue` now add their values in hash form, i.e.
`<option ng-value="myString">` becomes `<option ng-value="myString" value="string:myString">`.

This is done to support binding options with values of any type to selects.

This should rarely affect applications, as the values of options are usually not relevant to the
application logic, but it's possible that option values are checked in tests.
Narretz added a commit to Narretz/angular.js that referenced this issue Jun 30, 2016
select elements with ngModel will now set ngModel to option values added by ngValue.
This allows setting values of any type (not only strings) without the use of ngOptions.

Interpolations inside attributes can only be strings, but the ngValue directive uses attrs.$set,
which does not have any type restriction. Any $observe on the value attribute will therefore receive
the original value (result of ngValue expression). However, when a user selects an option, the browser
sets the select value to the actual option's value attribute, which is still always a string.
For that reason, when options are added by ngValue, we set the hashed value of the original value in
the value attribute and store the actual value in an extra map. When the select value changes, we
read access the actual value via the hashed select value.

Since we only use a hashed value for ngValue, we will have extra checks for the hashed values:
- when options are read, for both single and multiple select
- when options are written, for multiple select

I don't expect this to have a performance impact, but it should be kept in mind.

Closes angular#9842
Closes angular#6297

BREAKING CHANGE:

`<option>` elements added to `<select ng-model>` via `ngValue` now add their values in hash form, i.e.
`<option ng-value="myString">` becomes `<option ng-value="myString" value="string:myString">`.

This is done to support binding options with values of any type to selects.

This should rarely affect applications, as the values of options are usually not relevant to the
application logic, but it's possible that option values are checked in tests.
Narretz added a commit to Narretz/angular.js that referenced this issue Jul 1, 2016
select elements with ngModel will now set ngModel to option values added by ngValue.
This allows setting values of any type (not only strings) without the use of ngOptions.

Interpolations inside attributes can only be strings, but the ngValue directive uses attrs.$set,
which does not have any type restriction. Any $observe on the value attribute will therefore receive
the original value (result of ngValue expression). However, when a user selects an option, the browser
sets the select value to the actual option's value attribute, which is still always a string.
For that reason, when options are added by ngValue, we set the hashed value of the original value in
the value attribute and store the actual value in an extra map. When the select value changes, we
read access the actual value via the hashed select value.

Since we only use a hashed value for ngValue, we will have extra checks for the hashed values:
- when options are read, for both single and multiple select
- when options are written, for multiple select

I don't expect this to have a performance impact, but it should be kept in mind.

Closes angular#9842
Closes angular#6297

BREAKING CHANGE:

`<option>` elements added to `<select ng-model>` via `ngValue` now add their values in hash form, i.e.
`<option ng-value="myString">` becomes `<option ng-value="myString" value="string:myString">`.

This is done to support binding options with values of any type to selects.

This should rarely affect applications, as the values of options are usually not relevant to the
application logic, but it's possible that option values are checked in tests.
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

4 participants