ngModel binding to array values

20,347 views
Skip to first unread message

Peter Bacon Darwin

unread,
Oct 27, 2012, 3:59:09 PM10/27/12
to ang...@googlegroups.com
OK, so I need some help here.  I am binding ngModel to values in an array - unfortunately these values are strings and so are passed by value.


The first form displays the websites nicely but, since the website value is a string, changes in the input boxes are not reflected in the array.
The second form displays and binds correctly but the array seems to get rebuilt every time there is a change and so the input box loses focus each time to type a letter.
In the third form I have had to man-handle the data so that we are binding to an object instead of a string - I would like not to have to to this.

Anyone got any ideas how to get the first form to work the way I want?

Pete

Benny Lichtner

unread,
Oct 28, 2012, 4:01:51 AM10/28/12
to ang...@googlegroups.com
I have pretty similar question, Pete. (But no answer!)

Here are three attempts at using a directive to bind a text input to a model:

1) http://jsfiddle.net/u8nXT/ - model is a nested object
2) http://jsfiddle.net/qF6Yh/ - model is a nested object
3) http://jsfiddle.net/ta66J/ - model is an array of objects

Fiddles 1 and 2 have the focus issue you described, and fiddle 3 works and is similar to your third form.

--Benny

Benny Lichtner

unread,
Oct 28, 2012, 4:21:16 AM10/28/12
to ang...@googlegroups.com
It looks like the loss of focus has something to do with the ng-repeat directive. In this fiddle, everything works as expected: http://jsfiddle.net/roychoo/kMDPc/1/ (found here: https://groups.google.com/d/topic/angular/38q9pAXTxag/discussion).

--Benny

Peter Bacon Darwin

unread,
Oct 28, 2012, 3:58:22 PM10/28/12
to ang...@googlegroups.com
The loss of focus is that ng-repeat messes with the DOM every time a primitive value is updated.

I have to admit, I am rather confused. See this Plnk: http://plnkr.co/edit/ZSDnYt?p=preview
I thought the reference might be lost when the ng-model bound to the item but this Plnk shows that the compiler is clever enough outside the ng-repeat to update the primitive value.
Then I thought that the item in the child scope would get updated but this also shows that the child scope item is not being updated.

Any ideas?

Pete

--
You received this message because you are subscribed to the Google Groups "AngularJS" group.
To post to this group, send email to ang...@googlegroups.com.
To unsubscribe from this group, send email to angular+u...@googlegroups.com.
Visit this group at http://groups.google.com/group/angular?hl=en.
 
 

Witold Szczerba

unread,
Oct 28, 2012, 6:26:53 PM10/28/12
to ang...@googlegroups.com
The problem with your code:
http://plnkr.co/edit/ZSDnYt?p=preview
is quite simple. Let's try to analyze:
(1) you have myArray with 3 objects, these are String objects,
(2) ng-repeat iterates over each of them,
(3) for each item new scope is created,
(4) each new scope is given a property 'item', so you have:
(4a) scope.item = 'a'
(4b) scope.item = b'
(4c) scope.item = 'c'
(5) on top of this you create a input field with ng-model='item' and
button: scope.item = 'xx'.

OK, now let's try to reproduce what happens when you type in any of
those text field or press an 'update' button:
initially you have:
scope.item = 'a'
you press button and:
scope.item = 'xx' //or something else when you type.

Do you see it? You are not changing the content of myArray in any way!
You are creating new 'xx' String object and you re-assign the
"scope.item" which does not point to the myArray item any more. String
objects are immutable, one cannot "edit" the value of a String, you
can only replace one with another.

See this update:
http://plnkr.co/edit/AxOVSL?p=preview

Regards,
Witold Szczerba

Witold Szczerba

unread,
Oct 28, 2012, 7:25:28 PM10/28/12
to ang...@googlegroups.com
Going back to your initial question:

>The first form displays the websites nicely but, since the website value is a string, changes in the input boxes are not reflected in the array.
> [...]
> Anyone got any ideas how to get the first form to work the way I want?

the answer is: this is not possible unless you undermine the String
immutability in some magic way and force a String object to change the
value or you hack on ngRepeat so it would allow for meta data
migration from one object to another (it could watch the item property
and reapply its meta data to new object).
First options is out of the question, second is tricky and I guess it
could cause strange behavior sometimes (it could actually modify the
array), maybe performance issues as well?

Regards,
Witold Szczerba

Benny Lichtner

unread,
Oct 28, 2012, 9:05:17 PM10/28/12
to ang...@googlegroups.com
Thanks for looking at this Witold. I still don't understand why an array of objects doesn't suffer from the same focus issue that an object of objects does. Can you help me understand this? Do you understand this part, Pete?

Here's a version of the plunker that I would expect to work the same as your fix, Witold: http://plnkr.co/edit/23Dbjg?p=preview. And here's a plunker that does work, but it requires an extra level of nesting which I wouldn't think would be necessary: http://plnkr.co/edit/rb39Gn?p=preview. Isn't myJSON[key] (in the first plunker) the same type of object as myJSON[key].val (in the second plunker)? Is the difference that in the first case changing the value of one of the inputs changes the data structure over which ng-repeat iterates?

Peter Bacon Darwin

unread,
Oct 29, 2012, 2:35:32 AM10/29/12
to ang...@googlegroups.com

Hi Witold
Thanks for looking at this. I totally get the immutable string bit and why the array is not updated. What I don't understand is that the local scope variable 'item' does not get updated in the repeat block but the local scope variable 'myString' does.

Pete
...from my mobile.

Witold Szczerba

unread,
Oct 29, 2012, 6:53:54 AM10/29/12
to ang...@googlegroups.com
On 29 October 2012 02:05, Benny Lichtner <kak0iy...@gmail.com> wrote:
> Thanks for looking at this Witold. I still don't understand why an array of
> objects doesn't suffer from the same focus issue that an object of objects
> does. Can you help me understand this? Do you understand this part, Pete?

Hi,
The array of strings does suffer, because you are replacing array
elements. Again: in your code you ask ngRepeat to watch your array and
simultaneously you are replacing objects inside that watched array.
Once you replace an item, ngRepeat cannot figure what was the reason.
It thinks you have (a) removed an item and (b) inserted new item. Each
keystroke makes it think that way, so each time it destroys DOM node
and created new one.
When you have array of objects and you are not removing and replacing
array's objects, ngRepeat does not recreate DOM nodes.

Regards,
Witold Szczerba

Witold Szczerba

unread,
Oct 29, 2012, 7:00:40 AM10/29/12
to ang...@googlegroups.com
That is good question. Looks like in the example:
http://plnkr.co/edit/ZSDnYt?p=preview
something does confuse the "item" watchers in some way... but I do not
know why.
Does anyone know?

Regards,
Witold Szczerba

Peter Bacon Darwin

unread,
Oct 29, 2012, 7:52:25 AM10/29/12
to ang...@googlegroups.com
Interestingly, if you stick a new ng-controller inside the ng-repeat block then the {{item}} does update as expected...

Pete

Thierry Chatel

unread,
Oct 29, 2012, 4:00:08 PM10/29/12
to ang...@googlegroups.com
The 'item' seems confused for the same reason: item reference a string from the array. Initially the input field and {{item}} are binded to the same string, but modifying data in the input field creates a new string, distinct from the one binded to {{item}}. Same phenomenon with the button, which sets another string for 'item', but it does not affect the string(s) binded to {{item}} and to the input field. Each new string is also a distinct 'item'.

So ng-repeat may be used with an array of strings only if all binding are read-only ones, trying to modify an item in any manner just can't work.

Michael Dausmann

unread,
Nov 29, 2012, 5:03:48 PM11/29/12
to ang...@googlegroups.com
Great discussion.  after reading this I decided to cut my losses and use the wrap in an object method.  Its actually not that bad.  I used this handy dandy array method to make it pretty

$scope.myArrayWrapped = $scope.myStringArray.map(function(aStringValue){return {aStringValue:aStringValue};});

Michael
Reply all
Reply to author
Forward
0 new messages