3

I have a custom angular directive which for the sake of brevity we'll just call map.

It has an isolate scope set up like this.

scope: {
    latitude: '=?',
    longitude: '=?',
    draggable: '=?'
},

I normally put something in my link function which sets scope values to be either what has been set in the attribute, or a default if nothing has been set so the devoloper can add draggable="false" if they want, otherwise we'll automatically default it to true, so in the link function I would have this.

$scope.draggable = angular.isDefined($scope.draggable) ? $scope.draggable : myDefaults.draggable;

The problem is that I want all of the following to be valid uses in html

<map draggable></map>
<map draggable="true"></map>
<map draggable="false"></map>

but the first line won't work because angular.isDefined() is false.

Should I check whether it is defined first and if it isn't then use something like attrs.draggable to see if the attribute exists before falling back on the default?

If so then there is a further complication because I need to put my code in the controller not the link function (because it needs to talk to child directives) and I don't have access to "attrs" or "element" from the controller.

Any help would be much appreciated.

1 Answer 1

1

If you don't want to use scope variables in your draggable attribute, just use the $attrs parameter in the directive's link function for all four of your use cases:

link: function ($scope, $element, $attrs) {
    if ("draggable" in $attrs) {
        $scope.draggable = $scope.draggable === "" || $attrs.draggable === "true";
    } else {
        $scope.draggable = myDefaults.draggable;
    }
}

Explanation: If the draggable attribute doesn't exist, it will not be in the $attrs object. If it exists but is not set, it will be equal to empty string. If it exists and is set, it will equal the string value you set it to: "true" or "false".

If you use $attrs, don't worry about assigning draggable via the scope property of your directive -- it will get overwritten.

Note, as mentioned above, this solution won't allow you to bind scope variables to the attribute. For that, you'll have to use the scope property on your directive.

For the curious, the link function.

Sign up to request clarification or add additional context in comments.

9 Comments

Thanks, but will that be true for <map draggable></map> ? unless I am wrong I think that would be false
@jonhobbs For <map draggable></map>, $attrs.draggable will be undefined in the link function, in which case myDefaults.draggable will be used.
Yeah, that's what I thought. I need it to be true when draggable attribute exists but is not set, that's why I'm struggling :)
@jonhobbs I was wrong about what $attrs.draggable would equal when it existed but was not set. See my edited answer.
That's nearly right, but it should only use the default if the attribute is omitted. If it exists and is not set it should be true, if it is set then it should be true if it is anything other than draggable='false'. However there seems to be a bigger problem here because I am using a =? binding and the more I read the more it seems as if there is no way to use a two way binding AND allow a string/bool/expression to be used optionally, so it's not possible to have draggable="'false'" and draggable="controllerScopeVariable" using the same binding.
|

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.