Passing Boolean Value Into Directive

I am trying to toggle the visibility of an element rendered by a directive using <div ngHide="readOnly">. The value or readOnly is passed in via a directive.

angular.module('CrossReference')
    .directive('singleViewCard', [function() {
        return {
            restrict:'AE',
            templateUrl: '/CrossReference-portlet/js/templates/SingleViewCard.html',
            replace:true,
            scope: {
                readOnly:'@'

            },
            link: {
                pre:function(scope, tElement, tAttrs) {}, 
                post:function(scope, tElement, tAttrs) {};
                }
            }
        };
    }]);

This seems to work in the following cases:

<!-- element rendered in single-view-card is hidden -->
<div single-view-card read-only="{{true}}"/>

<!-- element rendered in single-view-card is shown -->
<div single-view-card read-only="{{false}}"/>

However, if I invert the boolean expression <div ngHide="!readOnly"> The following usage of the directive does not hide the dive as expected:

<!-- element is rendered when it should be hidden -->
<div single-view-card read-only="{{false}}"/>

What am I doing wrong?


Solution 1:

what you are doing wrong is

readOnly:'@'

this means readOnly will be a string, to make it a js variable try

readOnly:'='

then

<div single-view-card read-only="{{false}}"/>

should be

<div single-view-card read-only="true"/>

you need to show more code, this can be part of the error but I think there is more

hope it helps

Solution 2:

Currently it does not work because as bto.rdz mentionned in his answer, @ will read the raw value of the attribute, this is why you needed to interpolate your boolean value. In the following example, you can switch the value of readOnly at the directive's scope level through a controller holding the model ctrlReadOnly.

What I suggest instead is to use a scope variable to make it dynamic with =.

How to use your directive

<div single-view-card read-only="ctrlReadOnly"></div>

The directive looks like this

angular.module('CrossReference', [])
    .directive('singleViewCard', [function () {
    return {
        restrict: 'A',
        template: '<div ng-hide="readOnly">Hidden when readOnly is true</div>',
        replace: true,
        scope: {
            readOnly: '='
        }
    };
}])

Here is a fake controller to prepare the data for the view

.controller('myCtrl', function ($scope) {
    $scope.ctrlReadOnly = false;
});

I made a js fiddle here

Solution 3:

Your directive is fine (except that there shouldn't be a semicolon after post:function(scope, tElement, tAttrs) {}).

Directive usage:

<div single-view-card read-only="true"></div>

In SingleViewCard.html:

<div ng-hide="!{{readOnly}}">Lorem ipsum</div>

Note that the exclamation mark comes before the curly braces.

Explanation

scope: {
    readOnly:'@'
}, 

means that readOnly becomes a variable that holds a string value specified by the read-only attribute. We can then use our readOnly variable in the template by surrounding it with double curly braces.

I have also made a jsFiddle for it here.

Solution 4:

In the (post 1.5) component world, this can be achieved using '<' to pass the boolean along as a one-time binding.

Angular Components Reference

Inputs should be using < and @ bindings. The < symbol denotes one-way bindings which are available since 1.5. The difference to = is that the bound properties in the component scope are not watched, which means if you assign a new value to the property in the component scope, it will not update the parent scope.