3

Is it possible when creating a custom directive to have an attribute which can be a string or two way bound to something on the scope?

So, for example if I have this in my directive declaration:

$scope: {
    position: '=?'
}

and then alert it in my link function or controller:

$alert($scope.position);

It works if I actually bind it to something on my parent scope, but if I just put a string in I get undefined unless I use single quotes inside the double quotes. E.g.

<my-directive position="'right'"></my-directive>

That way it evaluates the expression as a string, but It seems ugly. I'd rather be able to use position="right" when I want to give the attribute a string, or use position="{{scopeVariable}}" when I want to bind it to two way bind it to something in the parent controller.

Am I wrong to be using "=?" as the isolate scope binding? Is there a better way to do this? Or should I just get used to using single quotes inside double quotes?

1
  • It looks alright however I think it highlights the fact that the directive is maybe doing 2 things when it shouldn't - accepting either a string or a scope variable. Maybe looking at that that would help simplify things. I'm not sure on the best approach to the actual query though. Commented Apr 19, 2015 at 20:05

2 Answers 2

3

= according to Angular documentation represents two-way binding - it means you have to use quotes. If you are going to pass string your should use text binding:

$scope: {
    position: '@'
}

and than you can pass your variable like

<my-directive position="right"></my-directive>

or

<my-directive position="{{right}}"></my-directive>

but remember that it is one way binding.

If your really need 'hybrid' solution which is sometimes a text binding and sometimes two-way binding your could implement your binding manually

{
    scope: {}
    link: function(scope,element,attr){
        attr['position'] // the value of html attribute, use it directly or evaluate in parent scope
        scope.$parent.$eval(attr['position']); //evaluate variable 'right' in parent scope
    }
}
Sign up to request clarification or add additional context in comments.

Comments

2

The difference in @ and = syntax may seem confusing. But it is caused by the fact that the latter uses $parse to transform an expression. And the former uses $interpolate, which accepts expression-flavoured string and transforms it using $parse. So attribute with = is always an expression, and Angular doesn't need {{ }} there (it would be position="{{'right'}}" otherwise). And @ isn't limited to single expression or static string (I'm not sure that the manual mentioned this clearly).

This behaviour is buried deep inside $compile, and you can't override it in directive, $parse will throw an error because of invalid expression before anything.

To overcome this you need to reimplement = binding in controller or link. All we have to do is to $parse an expression against $scope.$parent, establish proper watcher and remove it on $destroy.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.