user1272965
user1272965

Reputation: 3074

Angular directive, refer to element content inside template

I'm trying to make a directive that conditionally adds a line break if an angular expression is non-blank. So far, I have this:

// this will show text with a <br> conditionally if the text is non-blank
angular.module('myApp.directives').directive('brIf', function () {
    return {
        scope: {
            text: '='
        },
        template: '<span ng-show="text && text.trim().length">{{ text }}<br/></span>'
    };
});

Which works if I use it like this (any part of the address might be undefined or blank):

<br-if text="address.number + ' ' + address.street + ' ' + address.apt"></br-if>

But what I really want is a directive I can use like this:

<!-- put a br at the end of all this jazz, only if the expression is not blank -->
<br-if>{{address.number}} {{address.street}} {{address.apt}}</br-if>

...to avoid all of the string math and the text= attribute. I know I can write a link function in my directive that gets at the html content via the element parameter (I think?), but I don't know how to use the contents of that element in the template. In other words, see my question inside the link function ...

// this will show text with a <br> conditionally if the text is non-blank
angular.module('myApp.directives').directive('brIf', function () {
    return {
        scope: {
            text: '='
        },
        link: function(scope, elm, attrs, ctrl) {
            // I want the text of elm here to be used instead of 'text' 
            // in my template below.  Is this possible?
        },
        template: '<span ng-show="text && text.trim().length">{{ text }}<br/></span>'
    };
});

Upvotes: 2

Views: 4294

Answers (2)

Jason Axelson
Jason Axelson

Reputation: 4685

You don't need a full transclusionFn for most cases. If you set transclude: true in your directive options, you can just add ng-transclude to your template where you want the content added:

<div class="example" style="background: blue; height: 30px; width: 30px" ng-transclude></div>

Reference: https://stackoverflow.com/a/16697788/175830

Upvotes: 2

tasseKATT
tasseKATT

Reputation: 38490

You can use transclusion:

app.directive('brIf', function($interpolate) {
  return {
    restrict: 'E',
    template: '<span ng-show="text && text.trim().length">{{ text }}<br/></span>',
    transclude: true,
    link: function(scope, element, attrs, controller, transclusionFn) {

      transclusionFn(scope, function(clone) {

        scope.text = $interpolate(clone[0].innerHTML)(scope);
      });
    }
  };
});

What you are after is clone which will be a fresh compiled copy of your transcluded content. This means that for example clone[0].innerHTML will be:

{{address.number}} {{address.street}} {{address.apt}}

You can then use the $interpolate service to compile the string into an interpolation function, use this function to compute the interpolated string against a scope and use the result:

var interpolationFn = $interpolate(clone[0].innerHTML);
var interpolatedString = interpolationFn(scope);
scope.text = interpolatedString;

Or simply:

scope.text = $interpolate(clone[0].innerHTML)(scope);

Demo: http://plnkr.co/edit/VNauZ0Kkr1HLCnsWTgyO?p=preview

Upvotes: 5

Related Questions