Observing a boolean attribute

3,595 views
Skip to first unread message

Dave Merrill

unread,
Jan 8, 2013, 12:42:08 PM1/8/13
to ang...@googlegroups.com
If I define a directive attribute that's conceptually a boolean like this:
  isDisabled: '='

Then I can pass it like this, and have it show up in the directive as an actual boolean, like I'd expect:
  is-disabled="state.isNavDisabled"

However, when the value of state.isNavDisabled in the calling scope changes, a $observe of 'isDisabled' doesn't get triggered. My guess is that that's because the attribute value end sup as a function under the hood, and that doesn't change, even if the value returned by the function does.

If instead I define it as '@', and pass it as is-disabled="{{state.isNavDisabled}}", then it's a string, so you have to do things in code like
  if (isNavDisabled === 'true')
...which is kind of ugly, but doable.

How would you handle this? Deal with it as a string?

Related, is there any code in a directive that runs on every digest cycle, independent of any $observes you set up?

Thanks, as always,
Dave

Peter Bacon Darwin

unread,
Jan 8, 2013, 12:45:49 PM1/8/13
to ang...@googlegroups.com

$observe is only for interpolated attributes. Use $watch with $parse instead.

Pete
...from my mobile.

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

Joshua Miller

unread,
Jan 8, 2013, 1:32:45 PM1/8/13
to angular
Hello!

If you're passing in a boolean expression, then '&' works as well:

     scope: { isDisabled: '&' },

And then wherever in the directive...

     if ( angular.isDefined( scope.isDisabled ) && scope.isDisabled() ) { ... }

This way, the $parse is done for you, but you'll still have to $watch it.

Josh

Dave Merrill

unread,
Jan 8, 2013, 1:56:06 PM1/8/13
to ang...@googlegroups.com
Thank you both, got it.

So for anyone following on, What works well for me is this:

Directive scope definition:
  isDisabled: '&'

Passed in like this, where state.isNavDisabled is a boolean var in the scope:
  is-disabled="state.isNavDisabled"

Referenced in directive code like this
  if (scope.isDisabled()) //etc;  the & binding creates a function that returns the actual passed value

I didn't bother with the angular.isDefined( scope.isDisabled )check. Without it, scope.isDisabled() returns undefined, which is falsy, so we're good. In other cases, I've been defaulting missing attributes from a collection of default values, but this type of binding doesn't allow that, so you have to cope in other ways.


Re my other question, which has other applications, is there any code you can define within a directive that runs on every digest? My impression is no.

Thanks again,
Dave Merrill

Joshua Miller

unread,
Jan 8, 2013, 2:03:10 PM1/8/13
to angular
Hey Dave!

Glad we could help. As for the code that runs on every digest, what's the use case?

Josh

Dave Merrill

unread,
Jan 8, 2013, 2:26:49 PM1/8/13
to ang...@googlegroups.com
Use case is that my directive is separately $observing four caller scope vars passed in as attributes, and $watching the function attribute we've been talking about, all to set the vars referenced in its UI. That works fine, just wondering if there's a solution with a bit less code.

Dave


On Tuesday, January 8, 2013 2:03:10 PM UTC-5, Joshua Miller wrote:
Hey Dave!

Glad we could help. As for the code that runs on every digest, what's the use case?

Josh

Joshua Miller

unread,
Jan 8, 2013, 2:51:36 PM1/8/13
to angular
I'm still not clear. Are you trying to replace the several $observes and $watches by a single call that runs during the $digest? If so, the digest only runs _because_ of the $observes and $watches, so I'm not sure how that could work. I'm sure I just misunderstood.

Josh

Dave Merrill

unread,
Jan 8, 2013, 3:12:50 PM1/8/13
to ang...@googlegroups.com
Well this is a fantasy/thought experiment, but suppose directives had a customDigest() attribute, similar to link(), but that ran during every digest. Rather than individually watching or observing a bunch of vars that are already tied into the digest cycle -- i.e., a digest already runs when any of them change -- you could supply a bit of code to run at that time. In my case I'd update the directive's internal scope vars, like I do now, but without adding the observers to trigger it.

Make sense? Or am I out in left field?

Dave

Venkatraman Dhamodaran

unread,
Jan 9, 2013, 1:31:25 AM1/9/13
to ang...@googlegroups.com
Hi Dave

> Rather than individually watching or observing a bunch of vars that are already tied into the digest cycle 

If your main motive is to reduce the number of $watches, you can create a expression combining all the scope variables and watch that expression.

For example

$scope.$watch("varA + varB + varC + varD" , function() {
   //perform the action.
});


-Venkat

Dave Merrill

unread,
Jan 9, 2013, 8:21:56 AM1/9/13
to ang...@googlegroups.com
Hi Venkat, I get that, was wondering about a more general approach, but I suspect it doesn't exist, for better or worse.

Any idea whether that one $watch on the concatenated string is more or less efficient than say, 4 separate $observes on the individual properties?

Dave Merrill

Dave Merrill

unread,
Jan 9, 2013, 9:55:40 AM1/9/13
to ang...@googlegroups.com
I'm doing something stupid, but I tried this:
  scope.$watch('' + scope.lastStartRow + ' ' + scope.lastEndRow + ' ' + scope.rowsPerPage + ' ' + scope.rowCount);

...and got a series of errors, culminating in this one:
  Token 'undefined' is an unexpected token at column 11 of the expression [undefined undefined undefined undefined] starting at [undefined undefined undefined]

Tried the same thing with attrs.lastStartRow etc, same result.

These are all '@' bindings, and code before the $watch sets default values in the attrs scope for any that aren't passed. Everything is working fine, except for this.

This seems like a good tool to have available, whether I use it in my specific case or nor. What am I missing?

Thanks,
Dave

On Wednesday, January 9, 2013 1:31:25 AM UTC-5, Venkat wrote:

Venkatraman Dhamodaran

unread,
Jan 9, 2013, 10:31:37 AM1/9/13
to ang...@googlegroups.com
Firstly, If '@' binding is used, $watch will not solve your problem. Because string is being passed into the directive as scope variables. 
So, Use '&' or '=' bindings  with $watch. 
Read about directive definition object in http://docs.angularjs.org/guide/directive

Secondly, $watch will watch the scope variables. It accepts string/function as first param. String will be evaluated as angular expression.

Modify your $watch as follows if use "=" binding
scope.$watch('lastStartRow + lastEndRow + rowsPerPage + rowCount' , function () {
  console.log(lastStartRow);
});

Modify your $watch as follows if use "&" binding
scope.$watch('lastStartRow() + lastEndRow() + rowsPerPage() + rowCount()' , function () {
  console.log(lastStartRow());
});


-Venkat

--

Venkatraman Dhamodaran

unread,
Jan 9, 2013, 10:36:20 AM1/9/13
to ang...@googlegroups.com
> Any idea whether that one $watch on the concatenated string is more or less efficient than say, 4 separate $observes on the individual properties?

IMO, $watch with concatenated scope variables expression should be much simpler, easy to maintain and efficient.  [provided the logic in $watch listener is simple]

-Venkat
Reply all
Reply to author
Forward
0 new messages