7

In AngularJS, I have the following scenario where a directive can accept an optional boolean parameter which should default to true by default, whenever it is not specified.

Example:

<my-directive data-allow-something="false">
    ... this works as expected as no default value should be set in this case ...
</my-directive>

<my-directive>
    ... but in this case i'd like allowSomething to equal true  ...
</my-directive>

I've tried the following approach, but for some reason the true value doesn't stick on allowSomething. making it a '=?' optional two way binding doesn't work neither as my passed value should be a concrete true/false and not a field reference.

angular.module('myApp').directive('myDirective') {
    ...
    controller: function($scope){
        if (!$scope.allowSomething)
            $scope.allowSomething = true;
    },
    ....
    scope: function(){
        allowSomething: '@'
    }
    ...
}

I'm sure there should be a simple way to achieve this, so what am i missing?

The solutions given at the following ticket: AngularJS directive with default options are not sufficient for my needs since the $compile function will prevent the link function from working. plus, the passed-in boolean value is not a reference type and i cannot give it a two-way binding.

R3tep
  • 12,512
  • 10
  • 48
  • 75
kob490
  • 3,177
  • 4
  • 25
  • 40
  • It could be because controller is instantiated before binding the isolated scope to the directive element. So the value you set would have got overwritten by the new scope created for the directive from the bindings. Try setting defaults in a $timeout in the controller or try doing it in the link function or call the directive controller function to set the value from link function. Also on another note: you have allowSomething as text binding and you are setting boolean as default to it, what you set in the attribute will arrive as text "false"? – PSL Aug 05 '15 at 17:51
  • tried to put this in the link function with no luck. I'm also trying to avoid the $timeout approach. i think there should be a cleaner way to do this. – kob490 Aug 05 '15 at 18:02
  • possible duplicate of [Angular directive with default options](http://stackoverflow.com/questions/18784520/angular-directive-with-default-options) – yvesmancera Aug 05 '15 at 18:22

4 Answers4

21

I think a better way to check that value is look for an undefined value like this:

controller: function($scope){
    if (angular.isUndefined($scope.allowSomething))
        $scope.allowSomething = true;
}

I had the same issue once and this worked for me. I think the best method is to use angular's methods for handling things.

Michael Sheely
  • 961
  • 2
  • 10
  • 31
  • also if you do `if (!$scope.allowSomething) $scope.allowSomething = true;` it will set it true when you explicitly set it to false. – Daniel Kobe Dec 09 '15 at 02:18
  • Instead of `$scope`, I use `$attrs` like `if (!$attrs.hasOwnProperty('allowSomething'))`. – jsruok Aug 04 '22 at 09:36
4

This is how I have been doing it:

html:

<my-directive allow-something="false"></my-directive>
<my-directive></my-directive>

directive:

angular
.module('app')
.directive('myDirective', function() {

var defaults = {
  allowSomething: true
};

return {
  restrict: 'E',
  templateUrl: '',
  scope: {},
  compile: function() {
    return {
      pre: function(scope, el, attrs) {
        scope.allowSomething = attrs.allowSomething || defaults.allowSomething;
      },

      post: function(scope, el) {
        // this is link
        // scope.allowSomething = default or whatever you enter as the attribute "false"
      }
    };
  }
};
}

The pre is fired before anything happens then the post is like the link function. This has allowed me to do dynamic things based on the attributes I set.

cneu
  • 51
  • 4
  • I think this is cleaner than accepted answer, also I've seen some known plugins using this pattern with a `defaults` object containing default configurations. – Alisson Reinaldo Silva May 10 '17 at 21:36
0

I think you can do something like this:

 scope : true,
  link : function(scope, element, attrs){
    scope.allowSomething = $parse(attrs.allowSomething)(scope) || true;
    console.log(scope)
  }
Qi Tang
  • 1,165
  • 6
  • 17
  • this will not work, as I'd like to keep the scope of the directive isolated, as it should be an independent reusable directive. – kob490 Aug 05 '15 at 18:41
0

Given your template is

<body>
  <my-directive></my-directive>
  <!-- <my-directive allow-something="false"></my-directive> -->
</body>

Then you can either use link (preferred if no interaction with other directives is needed) or controller.

angular.module('myApp', [])
  .directive('myDirective', function() {
    return {
      scope: {},
      link: function(scope, element, attrs) {
        scope.allowSomething = ('allowSomething' in attrs) ? attrs.allowSomething : true;
      }, 
      template: '<div>allow:{{ allowSomething }}</div>'
    };
  });  

// or

angular.module('myApp', [])
  .directive('myDirective', function() {
    return {
      scope: {
        allowSomething: '@'
      },
      controller: function($scope, $timeout) {
        $timeout(function() {
          if (angular.isUndefined($scope.allowSomething)) {
            $scope.allowSomething = true;
          }
        });
      },  
      template: '<div>allow:{{ allowSomething }}</div>'
    }; 
  });  
Mikko Viitala
  • 8,344
  • 4
  • 37
  • 62