Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I check whether an expression attribute has been provided to a directive?

I've created a directive that accepts a callback as an attribute, e.g:

<my-directive callback-expression="someFunction()"> </my-directive>

The directive is reusable and hence I've given it an isolate scope. I want to show a button within the directive based on whether that callback-expression attribute is set.

App.directive('myDirective', function(){
  restrict: 'E',
  scope:    {
              callbackExpression: '&'
            },
  template: '<button ng-show="!!callbackExpression">Fire callback</button>'
});

The problem is, it's a function even if the expression is empty:

console.log($scope.callbackExpression) with a blank attribute results in:

function (locals) {
  return parentGet(parentScope, locals);
}

My current solution is to have this line at the top of my link function:

if (attributes.callbackExpression) scope.callbackButton = true

Then ng-show on callbackButton

Are there any alternatives not requiring that extra line & scope property?

like image 571
Ed_ Avatar asked Nov 13 '13 19:11

Ed_


2 Answers

If you want to avoid putting anything on the stack then you can use the link function where you can access the attributes via attrs. Here are two approaches to that:

Link function option 1:

Instead of using template, you'd use this link function in your directive which conditionally adds your template:

link: function (scope, element, attrs) {     
     if (attrs.callbackExpression) {
         var html = '<button>Fire callback</button>';
         element.replaceWith(html);
     }
}

Option 1 demo: http://jsfiddle.net/ZC4MZ/2/

Link function option 2 (better for large templates):

For large templates you can use $templateCache. First you add the template:

myApp.run(function($templateCache) {
  $templateCache.put('myDirective.html', '<button>Fire callback</button>');
});

Then use it conditionally just like option 1 but with a $templateCache.get():

link: function (scope, element, attrs) {     
    if (attrs.callbackExpression) {
        var html = $templateCache.get('myDirective.html');
        element.replaceWith(html);
    }
}

Making sure to inject $templateCache into your directive:

myApp.directive('myDirective', function ($templateCache) {

Here's a demo using $templateCache: http://jsfiddle.net/ZC4MZ/3/

Option using just the template:

To use the template you'll need a variable on the scope. For this you can keep everything as you have it, just add:

link: function(scope, element, attrs) {
      scope.callbackExpression = attrs.callbackExpression;}
}

Template/scope variable demo: http://jsfiddle.net/ZC4MZ/5/

like image 74
KayakDave Avatar answered Oct 30 '22 16:10

KayakDave


You can use the $attrs object that you can inject into in your directives to get to this information.

Markup:

  <body ng-app="myApp" ng-controller="MyController">
    <my-directive text="No Expression"></my-Directive>
    <my-directive text="Expression" callback-expression="myCallback()"></my-Directive>
  </body>

JS:

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

  return {
    restrict: "E",
    scope: {
      text: '@',
      callbackExpression:'&'
    },
    templateUrl: "partial.html",
    link: function($scope, $elem, $attrs) {
      $scope.expressionCalled = false;
      if ($attrs.callbackExpression) {
        $scope.expressionCalled = true;
      }
    }
  }

});

I created a working plunk for this example: http://plnkr.co/edit/K6HiT2?p=preview

like image 38
Mike Ohlsen Avatar answered Oct 30 '22 18:10

Mike Ohlsen