Passing an object as a directive attribute

16,722 views
Skip to first unread message

Dave Merrill

unread,
Jan 2, 2013, 5:54:58 PM1/2/13
to ang...@googlegroups.com
I'd like to pass a static or $scope-d object to a tag directive as an attribute. Is that possible?

I've tried these, among other variations:
  <my-directive map="{a: 1, b: 2}"...

  <my-directive map="{{{a: 1, b: 2}}}"...

  $scope.map = {a: 1, b: 2};
then
  <my-directive map="map"...
or
  <my-directive map="{{map}}"...

I've tried declaring the attribute in the directive like this:
  scope: {map: '@'}
and
  scope: {map: '&'}

None of those appear to work. I'm sure I'm missing something obvious. Any thoughts?
Dave

Witold Szczerba

unread,
Jan 2, 2013, 6:08:29 PM1/2/13
to ang...@googlegroups.com
Hi,
create a jsFiddle and you will get an answer in no time. It is so easy to make a mistake coding in e-mail.

Regards,
Witold Szczerba


Dave

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

Tucker W

unread,
Jan 2, 2013, 7:28:24 PM1/2/13
to ang...@googlegroups.com
Could you use scope.$eval?

Dave Merrill

unread,
Jan 3, 2013, 1:35:44 AM1/3/13
to ang...@googlegroups.com
It's true that scope.$eval does work, converting the passed string to an object. 

We don't have to do that with simple types. Is that the state of the art, or is there some other way to pass an object into a directive as an attribute?

Thanks,
Dave

Joshua Miller

unread,
Jan 3, 2013, 2:43:48 AM1/3/13
to angular
Hi Dave!

Do you mean just passing an object variable off the scope to a directive through an attribute? Like this: http://jsfiddle.net/joshdmiller/FHVD9/

Josh


--

nsha...@gmail.com

unread,
Jan 3, 2013, 3:06:22 AM1/3/13
to ang...@googlegroups.com
I'm trying to do the same thing, creating a generic dialog loader. I have the different templates working, but I want to dynamically set the scope variables, depending on what's passed in:

<a load-dialog="{hostname:'{{current_hostname}}', channel:'{{current_channel}}'}">

then something like:

...
directive(...) {
 return {
        scope: true,
link: function($scope, element, attrs) {
element.bind('click', function() {
$scope.$eval(attrs.loadDialog);
}
...

Dave Merrill

unread,
Jan 3, 2013, 1:39:26 PM1/3/13
to ang...@googlegroups.com
Hmmm, using '='' as the scope character, as you did, gets it to work. Even passing an object literal works that way, sort of. Take a look at this fork:

It has three instance of the directive on the page, and though they all display correctly, if you enable the console you'll see a "10 $digest() iterations reached" error. Without the object literal instance it works fine. Using '@' instead of '=' doesn't overflow, but also doesn't work, for any of those instances.

There's no need in my case to pass an object literal, it was just handy, so the overflow problem with doing that isn't a big deal. Creating a scope var and passing it in does seem better than using $eval.

Can you explain why '=' makes this work? The docs (which I am reading btw!) say it makes the binding bi-directional, but I don't get the connection, or how you knew to do that.

Dave

On Thursday, January 3, 2013 2:43:48 AM UTC-5, Joshua Miller wrote:
Hi Dave!

Do you mean just passing an object variable off the scope to a directive through an attribute? Like this: http://jsfiddle.net/joshdmiller/FHVD9/

Josh

Tucker W

unread,
Jan 3, 2013, 2:08:57 PM1/3/13
to ang...@googlegroups.com
I'm not entirely sure why '=' causes errors when passing in an object literal but my guess is that it fails if the object passed in isn't part of the scope.  Here's a helpful video (linked to the scope portion) - this helped me much more than the docs did...

Joshua Miller

unread,
Jan 3, 2013, 2:10:24 PM1/3/13
to angular
The docs could probably be a little bit clearer here, but the basic idea is that '@' evaluates the attribute and keeps it in sync on the directive's scope, but it doesn't take a variable directly, so you're just passing in the object string; '&' evaluates an expression to a function accessible on the directive's scope, but the expression is evaluated relative to the _directive's_ scope, so it doesn't have access to the parent's scope property; finally, '=' just passes a property from the parent scope into the directive and keeps it in sync, which is what you want in this case.

For what it's worth, you could use '&' for the object literal: http://plnkr.co/edit/uIyPrk?p=preview. The key is that '&' creates a function from the expression, which must then be called to retrieve the evaluated object.

Josh


--

Joshua Miller

unread,
Jan 3, 2013, 3:07:34 PM1/3/13
to angular
Sorry, there's a typo in my reply. Using '&', the expression is evaluated using the _parent_ scope, not the scope of the directive. You could use this to pass a variable from the parent scope and access it by calling the resulting function, as in the above example with a literal, but this is what '=' is for.

Josh

Dave Merrill

unread,
Jan 4, 2013, 11:51:14 AM1/4/13
to ang...@googlegroups.com
So @ doesn't evaluate, but = and & do, right? I didn't quite get that the first times I read the docs about this.

One consequence of =  and & bind appears to be that those attributes aren't settable within your linking function., which makes sense, since it's bound ot the value whose name is passed in the attribute. Given that, how do you set default values for those attributes, to be used if they're not passed.

In the example here:
...the title attribute can be defaulted if it's not passed, but setting attrs.prop in the linking function appears to change it if you log it right there, but it has no effect when the directive renders.

How would you go about setting default values for complex attributes passed with = then?

Dave


On Thursday, January 3, 2013 2:10:24 PM UTC-5, Joshua Miller wrote:
The docs could probably be a little bit clearer here, but the basic idea is that '@' evaluates the attribute and keeps it in sync on the directive's scope, but it doesn't take a variable directly, so you're just passing in the object string; '&' evaluates an expression to a function accessible on the directive's scope, but the expression is evaluated relative to the _directive's_ scope, so it doesn't have access to the parent's scope property; finally, '=' just passes a property from the parent scope into the directive and keeps it in sync, which is what you want in this case.

For what it's worth, you could use '&' for the object literal: http://plnkr.co/edit/uIyPrk?p=preview. The key is that '&' creates a function from the expression, which must then be called to retrieve the evaluated object.

Josh

Joshua Miller

unread,
Jan 4, 2013, 4:00:38 PM1/4/13
to angular
Hi Dave!

I guess you _wouldn't_ go about setting default values. It's a two-way binding with '=' to the parent scope, so you shouldn't be able to create it spontaneously in a child scope (i.e. the directive). If you need it to be optional, I suppose the best approach would be to pass in an empty object through '=' so you can manipulate its missing properties: http://jsfiddle.net/joshdmiller/nSseE/4/.  Any other solution would make the property irretrievable in the parent scope, defeating the purpose of the two-way binding. However, if the two-way binding isn't needed, you should use '@' and pass in an expression (like `prop="{{val}}"`) which you could manipulate when there is no default as it's just a string.

Josh

Venkatraman Dhamodaran

unread,
Jan 4, 2013, 7:45:42 PM1/4/13
to ang...@googlegroups.com
Hi Dave,

If the usage of "=" is mandatory, new scope member can be created by merging the your default values and passed obj. And use the new scope member in the template.

Check the updated fiddle


-Venkat

Joshua Miller

unread,
Jan 4, 2013, 8:00:13 PM1/4/13
to angular
Hello!

Venkatraman is right, but the caveat I supplied still applies: this completely breaks bi-directional data binding, which is the entire purpose of '='. Under that example, neither objects that were passed nor those that weren't can propagate changes to the scope where they were defined (at least not without hacking a couple of $watches). As I said, if data binding is not required, then '@' or '&' are the way to go, philosophically.

Josh

Dave Merrill

unread,
Jan 5, 2013, 8:29:43 AM1/5/13
to ang...@googlegroups.com
@Josh and Venkat, thanks for the ideas.

I don't need two-way binding, so breaking bidirectionality isn't a problem.. I was only using = to allow a complex object to be passed as an attribute, without doing $eval on a passed string version of it.

I'm not sure if you could set the properties of an empty object passed through =; kind of doubt it, since the object is bound. But since the point is not to require callers to pass seldom-used configs at all, I don't want to require them to pass the empty config object either.

I've decided that in my immediate case, passing an object is more trouble than it's worth. I'm passing individual attributes instead, so missing ones can easily be defaulted/

If you're interested, here's a fiddle testing different strategies for doing that defaulting, only one of which, setting missing attrs, only, works correctly:

Dave

Venkatraman Dhamodaran

unread,
Jan 5, 2013, 11:56:20 AM1/5/13
to ang...@googlegroups.com
Hi Dave,

As Joshua suggested, & can be used to solve your problem [No two way binding, but want to pass complex object].

Updated the jsfiddle to use &


-Venkat

Joshua Miller

unread,
Jan 5, 2013, 1:08:29 PM1/5/13
to angular
Hi Dave!

If you don't need two-way binding and you know in advance which attributes you need and there aren't too many, then I think you're right: string-based scoping (`@`) is probably the right way to go. It's just simpler.

Josh

Kumar Harsh Srivastava

unread,
Jan 7, 2014, 8:39:02 AM1/7/14
to ang...@googlegroups.com
Hi Josh, on a related topic, I had a doubt:

There are instances when passing an object to the directive by manually writing it as the value of an attribute may be useful, like passing initial options to the directive, like this (a bad example, but just bear with me)

    <div data-ng-repeat="test in data.tests">
        <span data-xx-colorize data-xx-colors="{hue: {{5*$index}}, sat: {{50+10/$index}}, lum: 50}">
          {{test}}
       </span>
    </div>


Now, if I do bind it to my scope within the directive as so:

    directive('xxColorize', function() {
         ...
        scope: {
           color:  "&xxColors"
        }
        controller = function($scope, ...) {
           myColor = $scope.color();
           ...
        }
    });


But this style of usage has one fault... It will necessarily create an isolated scope.
What if I do NOT want any scope at all...

Is there a way to get the object to my directive without creating an isolates scope?
I know JSON.parse($attrs.xxColorize) does the job, but it would fail if the oject is not correctly formed (string keys, etc).
This problem does not happen in the method of binding with '&xxColorize', but sadly that is what I'm trying to avoid.

Is there another way to do it?

Daniel Tabuenca

unread,
Jan 7, 2014, 9:59:28 AM1/7/14
to ang...@googlegroups.com
Yes, just do $scope.$eval($attrs.xxColorize).

Kumar Harsh Srivastava

unread,
Jan 7, 2014, 3:28:24 PM1/7/14
to ang...@googlegroups.com
Thanks!

Witold Szczerba

unread,
Jan 7, 2014, 4:27:59 PM1/7/14
to ang...@googlegroups.com

...or $scope.$watch($attrs. xxColorize, function(xxColorize) {...}, true)
if the value is dynamic, like when it uses other values which may change. I am no sure if the last argument "true" is necessary.

Regards,
Witold Szczerba
---
Sent from my mobile phone.

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.

Md Kamrul Islam

unread,
Jan 7, 2014, 6:34:50 PM1/7/14
to ang...@googlegroups.com
Use

  $scope.map = {a: 1, b: 2};
then
  <my-directive map="map"...

and 
scope: {map: '='}
Reply all
Reply to author
Forward
0 new messages