How to $watch nested elements in arrays?

17,627 views
Skip to first unread message

florin

unread,
Jan 27, 2012, 10:44:17 AM1/27/12
to AngularJS
If a controller has self.[object*].object.attribute, how do I watch
for changes of object attributes?

Vojta Jina

unread,
Jan 27, 2012, 5:31:09 PM1/27/12
to ang...@googlegroups.com
Hey florin,

what is [object*] ???

You can watch anything:
scope.object = {
  attr1: 1,
  attr2: 2
}
scope.$watch('object', function() {});
scope.$watch('object.attr1', function() {});
scope.$watch(function() {return scope.object;}, function() {});

If this is not what you are looking for, please send us a jsfiddle...

V.
Message has been deleted

florin

unread,
Jan 27, 2012, 8:52:40 PM1/27/12
to AngularJS
An array.
scope.parent = [
{child: {name:'joe',age:5}
];
I'd like to $watch the child age grow.
can't write
scope.$watch("parent[??].child.age").
My app needs to keep track of a large number of item changes. I try
to
avoid boilerplate.

Igor Minar

unread,
Jan 27, 2012, 8:54:56 PM1/27/12
to ang...@googlegroups.com
scope.$watch("parent[0].child.age", listener);


/i

--
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.
For more options, visit this group at http://groups.google.com/group/angular?hl=en.


Vojta Jina

unread,
Jan 27, 2012, 9:03:26 PM1/27/12
to ang...@googlegroups.com
Of course you can.

Send code example on jsfidlle if you need more help.

V.

florin

unread,
Jan 27, 2012, 9:30:22 PM1/27/12
to AngularJS
It's not only for a certain array location, i.e. [0]. It's for any
element in the array, and the array length may change too.

scope.parent = [
{child: {name:'joe',age:2},
{spouse: {name:'pretty', age:24},
{child: {name:'joy', age:3}
];

Thanks.

Vojta Jina

unread,
Jan 27, 2012, 9:56:27 PM1/27/12
to ang...@googlegroups.com
Then, watch the whole array:
scope.$watch('parent', listenFn);

It would really help, if you send some real use case. I guess you need to know, what has changed as well. Then, I would write a custom watch function instead.
scope.$watch(function() {
  // check the object on its own
}, listenFn);

V.

florin

unread,
Jan 27, 2012, 10:51:09 PM1/27/12
to AngularJS
http://jsfiddle.net/floring/eQGXA/

I'd like to $watch the age update in the form field.

Igor Minar

unread,
Jan 27, 2012, 11:10:37 PM1/27/12
to ang...@googlegroups.com
this is a quite complex observation you are trying to create and as such it requires considering many factors.

for example what do you expect to receive as the value in your watch? a map of indexes of array elements that had their age changed, mapped to the new value. e.g. {1: 54, 5: 23} ?

Also how should this behave if a new element is added to the array or when its removed? should it be considered as a change to the age or is the array size fixed?

To me it feels like you are trying to do something that should be addressed differently.

Maybe all you need is to use ng:change on the input that is bound to the age. Or maybe even something completely different.

/i



for example, what is the format of th

--
You received this message because you are subscribed to the Google Groups "AngularJS" group.
Message has been deleted

florin

unread,
Jan 27, 2012, 11:31:38 PM1/27/12
to AngularJS
Safari and Chrome appear to chop these messages.

florin

unread,
Jan 27, 2012, 11:41:39 PM1/27/12
to AngularJS
Naively, what I need is both the updated age of the child and the
outcome of the array size change.

This is a pattern I need to solve. I have dozens of such attributes
that need to be monitored and my code is getting ugly. If there is a
work around, I'd love to see it.

Vojta Jina

unread,
Jan 28, 2012, 12:58:29 AM1/28/12
to ang...@googlegroups.com
0.10.6 allows deep watching, so you can do this: http://jsfiddle.net/vojtajina/eQGXA/3/

If you need to distinguish which value changed, better to use custom watcher.

V.

florin

unread,
Jan 28, 2012, 7:45:07 AM1/28/12
to AngularJS
Perhaps the watch expression be implemented as regex or a css
selector?

johngouf

unread,
Jan 30, 2012, 8:37:45 AM1/30/12
to AngularJS
Hi there, in this example http://jsfiddle.net/johngouf/5FaP6/
The watch expression is indeed triggered, but the value on the time
field stays the same. Am I missing something?
BR

johngouf

unread,
Jan 30, 2012, 12:06:49 PM1/30/12
to AngularJS
Nevermind....it is Chrome's Javascript Console issue.

On 30 Ιαν, 15:37, johngouf <johngou...@gmail.com> wrote:
> Hi there, in this examplehttp://jsfiddle.net/johngouf/5FaP6/
> The watch expression is indeed triggered, but the value on the time
> field stays the same. Am I missing something?
> BR
>

florin

unread,
Jan 30, 2012, 4:52:06 PM1/30/12
to AngularJS
Actually I am quite surprised there is no documented solution nor
questions for this issue that I can find.

Displaying tabular data, in-place editing its values and aggregating
same numbers is the bread and butter of a CRUD application which is
the stated best use of angularjs.

The workaround I have is using multiple controllers on the same page.
This results in duplicating or fragmenting models.

I appreciate any suggestion.

Vojta Jina

unread,
Jan 30, 2012, 6:35:16 PM1/30/12
to ang...@googlegroups.com
Hey florin,

again, if you want more help, please send us some real example through jsfiddle.
So far, you send only one really basic one, which I did send you a simple solution for, so I'm not sure how can I help you now...

Displaying tabular data and in-place editing is definitely valid use case, but that's easy with Angular, I don't understand, why do you need multiple controllers for that.

V.

cam...@kera.io

unread,
Aug 29, 2012, 3:56:26 PM8/29/12
to ang...@googlegroups.com
I've updated this example for Angular 1.0.1 and it no longer seems to work:


What am I missing?

Pawel Kozlowski

unread,
Aug 29, 2012, 4:06:20 PM8/29/12
to ang...@googlegroups.com
hi!

On Wed, Aug 29, 2012 at 9:56 PM, <cam...@kera.io> wrote:
> I've updated this example for Angular 1.0.1 and it no longer seems to work:
>
> http://jsfiddle.net/RbCqR/1/
>
> What am I missing?

Here is the version updated to 1.0.1:
http://jsfiddle.net/pkozlowski_opensource/RbCqR/4/

The thing to note is the syntax of scope.$watch (described here:
http://docs.angularjs.org/api/ng.$rootScope.Scope):
- to "deep-watch" pass 'true' as the last argument,
- arguments to the watcher function are: newValue, oldValue, scope

Cheers,
Pawel

mauro...@gmail.com

unread,
Sep 6, 2012, 5:31:02 PM9/6/12
to ang...@googlegroups.com
I think I'd like to do the same thing: watch an array and get notified which item was changed. Something like this:
http://jsfiddle.net/NDSnQ/12/

Is this possible somehow? The problem with $watch(function(){ ??? }, listenFn) is that the ???-function needs to return the whole array, so how do I tell the listenFn which item has changed?

Thanks.

Cameron Westland

unread,
Sep 6, 2012, 5:32:16 PM9/6/12
to ang...@googlegroups.com
You can't at the moment. 

Cameron Westland

--
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.

mauro...@gmail.com

unread,
Sep 6, 2012, 5:41:43 PM9/6/12
to ang...@googlegroups.com, cam...@kera.io
Thanks for the quick reply! I realize there's a bunch of workaround for this small example. But how would I go about a larger app with this pattern? Dynamically instantiate a new controller for each (todo) item in the array and then somehow keep their item in sync with the array of items in the parent scope?

Cameron Westland

unread,
Sep 7, 2012, 6:56:46 AM9/7/12
to ang...@googlegroups.com
Mauro,

I'd do something like this: http://jsfiddle.net/NDSnQ/13/

I think you always want to call a controller method when you want to do something sophisticated. Two way data binding is good for keeping the scope in sync, but it's not good for responding specifically to specific changes deep in the scope tree.

-Cameron

--
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.

Andy Joslin

unread,
Sep 7, 2012, 9:39:07 AM9/7/12
to ang...@googlegroups.com, flo...@wisemart.com
Hi mauro,

Your example will also work if you create a new controller inside the ng-repeat scope - so you have access to the $scope.$index variable: http://jsfiddle.net/66MTM/

mauro...@gmail.com

unread,
Sep 7, 2012, 1:06:59 PM9/7/12
to ang...@googlegroups.com, flo...@wisemart.com
Thanks to both of you! Andy's solution was exactly what I was looking for.

Marcelo Alcantara

unread,
Sep 8, 2012, 4:45:50 AM9/8/12
to ang...@googlegroups.com, flo...@wisemart.com
Hi Andy,

Your solution was really what I was looking for, but I am just curious as I could not see in the documentation (and not understand trying to read the angular source) how the $index became the fourth parameter of the listener as the listener in the documention and source just seems to support the first 3 arguments. Maybe is it something that is missing from my JS knowledge?

 $scope.$watch('todos[$index]',
        function(newvaloldvalscopeindex{
            console.log(
                "todo with following text changed:",
                $scope.todos[$scope.$index].text
            );

Cheers
Marcelo

Pawel Kozlowski

unread,
Sep 8, 2012, 4:55:03 AM9/8/12
to ang...@googlegroups.com
H Marcelo!

There is not much mystery here really, as the 4th parameter to the
listener function is not defined and is not used :-)
I believe that this is just a left-over from one of the experiments,
the jsFiddle still works if you remove it:
http://jsfiddle.net/h4KGd/2/

Actually the index gets accessed using $scope.$index (and not index).
The $index variable is defined by the ngRepeat directive and is
documented here: http://docs.angularjs.org/api/ng.directive:ngRepeat

Hope this helps,
Pawel
> --
> 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.
>
>



--
Question? Send a fiddle
(http://jsfiddle.net/pkozlowski_opensource/Q2NpJ/) or a plunk
(http://plnkr.co/)
Need help with jsFiddle? Check this:
http://pkozlowskios.wordpress.com/2012/08/12/using-jsfiddle-with-angularjs/

Looking for UI widget library for AngularJS? Here you go:
http://angular-ui.github.com/

Marcelo Alcantara

unread,
Sep 8, 2012, 5:34:53 AM9/8/12
to ang...@googlegroups.com
Hi Pawel,

Sure! I missed that the index was not used at all. Just made me curious for why it was there.

I tried to apply the ideas on the fiddle to my problem but without much success.

My table is a transposed table where each record is a column. My ng-repeat produces the TDs and not the TRs and I need to make "value in collection.item" and item has an array of what should be in the line.

For the ng-repeat overriden controller to work for me I would need to make one controller for each line and it just doesn't make sense. Unless there was a was to specify on the call what would be the "item" array for that line in a parameter in the tag. Maybe a directive?

I will try to make a fiddle of the problem.

Cheers
Marcelo

p.kame...@gmail.com

unread,
Sep 13, 2012, 7:08:44 AM9/13/12
to ang...@googlegroups.com
Hi Pawel,

Is this example actually working? $watching array[$index] doesn't work here, also there's no $index defined on $scope...

Pawel Kozlowski

unread,
Sep 21, 2012, 4:30:14 PM9/21/12
to ang...@googlegroups.com
Hi!

On Thu, Sep 13, 2012 at 12:08 PM, <p.kame...@gmail.com> wrote:
> Hi Pawel,
>
> Is this example actually working? $watching array[$index] doesn't work here,

Hmm, if you ask me, this works:
http://jsfiddle.net/pkozlowski_opensource/mYYBL/3/
Check the console to see that a clicked item is correctly detected.

Unless something else is expected from this jsFiddle :-)

> also there's no $index defined on $scope...

Depends which scope we are talking about here. It _is_ defined in a
scope created by the ngRepeat (ngRepeat will create a scope for each
and every individual element it iterates over). The trick in this
jsFiddle is that a controller is specified for each item (and it gets
injected with a scope created by ngRepeat for each element).

Did you notice that there is both ng-repeat and the ng-controller on the <li>?

<li ng-repeat="todo in todos" ng-controller="TodoItemCtrl">

But once again, I was just cutting into this thread, commenting on the
$index usage so I might have missed the root of the problem here...

Cheers,
Pawel

Miha Valencic

unread,
Mar 26, 2013, 1:05:02 PM3/26/13
to ang...@googlegroups.com
Pawel, (or anybody), help me out, please :)

$scope.$watch(arrayOfObjects, function(oldVal, newVal){}, true); works, but the source code reads:

$watch: function(watchExp, listener, objectEquality) 

"true" should mean "deep" watching, but the source code defines that parameter as objectEquality. 

I am very puzzled:

1) the documentation does not mention deep watching: http://docs.angularjs.org/api/ng.$rootScope.Scope#$watch
2) if I look at the source code, the $watch function is defined with three parameters, where the last parameter defines objectEquality, not whether deepWatch or not
3) if the last parameter is "true", then it works for deep watching

So I'm puzzled...

If someone could clarify this for me...

Thanks,
 Miha.

Clint Checketts

unread,
Mar 26, 2013, 1:28:15 PM3/26/13
to ang...@googlegroups.com
objectEquality implies object contents equality, (the deep compare that checks deep attributes)

objectEquality of false means it is using referenceEquality

Maybe my definition of objectEquality is wrong. Are you asking that the documentation should be changed or that the variable names should be clarified?




--
You received this message because you are subscribed to the Google Groups "AngularJS" group.
To unsubscribe from this group and stop receiving emails from it, send an email to angular+u...@googlegroups.com.

To post to this group, send email to ang...@googlegroups.com.

Miha Valencic

unread,
Mar 26, 2013, 1:33:05 PM3/26/13
to ang...@googlegroups.com
On Tue, Mar 26, 2013 at 6:28 PM, Clint Checketts <chec...@gmail.com> wrote:
> Maybe my definition of objectEquality is wrong. Are you asking that the
> documentation should be changed or that the variable names should be
> clarified?

Hi Clint!

I obviously misunderstood what objectEquality was for. I was under the
impression that I can provide my own function for testing object
equality and that default one uses '=='... So, if I understand it
correctly now, objectEquality is a boolean value, indicating whether
to test two values using value or reference comparison?

One more thing: how can I get an "index" of the array element being
changed, if the watched element is an array? Is it at all related to
watch function?

Thanks,
Miha.

Clint Checketts

unread,
Mar 26, 2013, 1:51:14 PM3/26/13
to ang...@googlegroups.com
You are correct, it is a boolean to indicate checking the object's value, while false is object reference.

I don't think it exposes the index of any differences if an array changes, so you'd need to iterate along the oldVal and newVal  to find the difference,



Thanks,
 Miha.

Miha Valencic

unread,
Mar 26, 2013, 1:54:37 PM3/26/13
to ang...@googlegroups.com

On Tue, Mar 26, 2013 at 6:51 PM, Clint Checketts <chec...@gmail.com> wrote:
You are correct, it is a boolean to indicate checking the object's value, while false is object reference.

Thanks!

YSS

unread,
May 29, 2014, 6:27:36 PM5/29/14
to ang...@googlegroups.com
Can you give me an fiddle example like comparing two arrays, which will have same ids and return true or false for each object comparison using angular watch and for each.
Please find Stackoverflow question posted by me here

Douglas Lira

unread,
May 30, 2014, 8:06:23 AM5/30/14
to ang...@googlegroups.com

$watchCollection

Visit this group at http://groups.google.com/group/angular.
For more options, visit https://groups.google.com/d/optout.
Reply all
Reply to author
Forward
0 new messages