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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.