4
votes

I would like to create an Angular JS directive to check the length of a string, if it is too long to shorten it using a Filter, and show an Angular-UI popover on mouseover.

Where in the directive should I be placing the functionality to get this to work (link, template or compile)?

The view:

<div myapp-shorten="project">{{project.Description}}</div>

Here are my first attempts at the directive so far:

angular.module('myapp.directives', [])
 .directive('myappShorten', function () {

    function link(scope, element, attrs) {

        var outputText = "";

        if (myappShorten.Description.length > 20) {
            outputText += "<div popover='{{myappShorten.Description}}' popover-trigger='mouseenter'>" +
            "{{myappShorten.Description | cut:true:20:' ...'}}</div>";
        } else {
            outputText += "<div>{{myappShorten.Description}}</div>";
        }

        element.text(outputText);
    }

    return {
        link: link,
        scope: {
            myappShorten: "="
        }
    };

});
2
Perhaps a more comprehensive overview of directive functions: Angular directives - when to use compile, controller, pre-link and post-link.Izhaki

2 Answers

4
votes

First of all you can change the filter that it wouldn't alter string if it doesn't need to

Second, since you only need filter and popover - template is enough.

 angular.module('myapp.directives', [])
   .directive('myappShorten', function () {

     return { 
       scope: { data : '=myappShorten',
       template:"<div popover='{{data.Description}}' popover-trigger='mouseenter'>" +
        "{{ data.Description | cut:true:20:' ...' }}</div>"
     }
   })

Alternatively you can use combination of ng-show and ng-hide

 app.directive('shorten', function () {
return {
    restrict: 'A'
    , scope :  {
        shorten : '=',
        thestring: '='   
    }
    , template: "<div ng-show='sCtrl.isLong()' tooltip='{{ sCtrl.str }}'>{{ sCtrl.short() }}</div>"+
                "<div ng-hide='sCtrl.isLong()'>{{ sCtrl.str }}</div>"

    , controllerAs: 'sCtrl'
    , controller: function ($scope) {
        this.str = $scope.shorten || ''
        this.length = $scope.thestring || 20

        this.isLong = function() {
            return this.str.length > this.length   
        }

        this.short = function() {
            if ( this.str.length > this.length)  {
                return this.str.substring(0,this.length)  + '...'                    
            }
        }                                  
    }                               
  }       
})

Third option would be to actually use compile and $watch on myappShrten.Description but it seems to be overkill to me.

1
votes

The above accepted answer works fine. But if the value of thestring changes this will not update as the controller compiles on first run and then will not update if the value changes. Putting code into the controller compiles upfront, but putting the code in the link function allows it to update if the value changes. This is my preferred solution inspired by the solution above:

The view:

<shorten thestring="project.Description" thelength="40"></shorten>

The directive:

.directive('shorten', function () {
    return {
        restrict: 'E'
        , scope: {
            thelength: '=',
            thestring: '='
        }
        , link: function postLink(scope, iElement, iAttrs) {
            scope.isLong = function () {
                return scope.thestring.length > scope.thelength
            }

            scope.short = function () {
                if (scope.thestring.length > scope.thelength) {
                    return scope.thestring.substring(0, scope.thelength) + '...'
                }
            }

        }

        , template: "<div class='handCursor' ng-show='isLong()' tooltip='{{ thestring }}'>{{ short() }}</div>" +
                    "<div ng-hide='isLong()'>{{ thestring }}</div>"
    }
});