Angular Directive

Posted by Shi

Directive

Responsbilities

  1. Manipulate DOM
  2. Receive view events: translating view events into calls on the scope

Naming

Prefix your directive like “up”(upSearchResult) to avoid name collision

Purpose

  • Widget

  • DOM Events ng-click

  • Functionality ng-show

Sample directive 1

Directive:

myApp.directive("searchResult", function (){
    return{
        template:'<div></div>',
        templateUrl:'path/search-result.html',
        controller: 'SearchCtrl',
        controllerAs: 'vm',
        replace: 'true', // Replace all the properties in this tag (default is false) e.g. http://plnkr.co/edit/uQGHI1?p=preview 
        // Isolate the scope, local scope binding
        // If you delete this, this directive will share the parent's scope
        scope:{
            personName:"@" // @ for text, one way binding 
            personObject:"=" // = for object,  two way binding
            formattedAddressFunction:"&" // & for function 
        },
        transclude:true,
        
        //Include the parent directive
        require:'^nameOfParentDirective'
                
        //Link is a useful short version of compile-post (Put some function here for the directive to use)
        link: function(scope, elements, attrs, ctrl){//add controller of the parent directive as the last parameter
            scope.viewClassDetail = function(classToView){
                //do something
            }
        }

        compile: function(elem,attrs){
            console.log('Compiling...');
            //Edit html created
            elem.removeAttr('class');//remove classes in html
            return{
                pre:function(scope, elements, attrs){
                    //Avoid to use pre link! The date is 
                }

                post:function(scope, elements, attrs){
                    //Aready generated html; Do some modification here 
                    if(scope.personObject.name == "Jane Doe"){
                        elements.removeAttr('class');
                    }

                }

            }
        }
                
        restrict: 'AEC',//(A stand for Attribute; E for Element; C for Class; M for Commant), restrict this element to be used only when I use this in an element or attribute PS: default value is AE

    }
});

template search-result.html

    <div> {{ personName }}</div>
    <div> {{ personObject.name }}</div>
    <div> {{ formattedAddressFunction({ aperson: personObject }) }} </div>
    <div> <ng-transclude></ng-transclude></div> //replace the transclude contenent here
    <small ng-transclude></small>
   

Use template in main html:

Use as Element

    //Pass a string
    <search-result person-name="{{ person.name}}"> 
        Search results may not be valid.
    </search-result>    //Pass person.name from parent to variable personName (Note : normalize of person-name)
        
    //Pass an object
    <search-result person-object="person"> </search-result>
    
    //Pass a function 
    //the parameter here aperson is a simbolic parameter, it should be specified in template; we should pass Object Map (person-object="person") instead of pure object.
    <search-result person-object="person" formatted-address-function="formattedAddress(aperson)"></search-result>


or as Attribute

<div search-result> </div>
<button ngbk-focus ng-click="clickFocused()">
    I'm very focuse
</button>

The main controller could be

myApp.controller('mainController', ['$scope', '$log', function($scope, $log) {
    
    $scope.person = {
        name: 'John Doe',
        address: '555 Main St.',
        city: 'New York',
        state: 'NY',
        zip: '11111'
    }
    
    $scope.formattedAddress = function(person) {
      
        return person.address + ', ' + person.city + ', ' + person.state + ' ' + person.zip;
        
    };
    
}]);

Sample directive 2 (Real case usage)

JS

var app = angular.module('app', []);

app.value('scFollowedInstructors', []);

app.controller('scInstructorsCtrl', function($scope, scFollowedInstructors) {
  $scope.followedInstCount = scFollowedInstructors.length;

  this.followInstructor = function(instructor) {
    scFollowedInstructors.push(instructor);
    $scope.followedInstCount = scFollowedInstructors.length;
  }
});

app.directive('scInstructors', function() {
  return {
    restrict: 'E',
    replace: true,
    template: '<div class="well sidebar-nav">' +
        '<h3>Instructors ({{followedInstCount}} followed)</h3>' +
        '<div class="row" ng-repeat="instructor in instructorList">' +
        '<div class="col-md-6">{{instructor.name}}</div>' +
        '<div class="col-md-6"><sc-follow-instructor instructor-to-follow="instructor" /></div>' + //Pass "instructor-to-follow" to the scope here
        '</div>' +
        '</div>',
    controller: 'scInstructorsCtrl'
  }
});

app.directive('scFollowInstructor', function(/*scFollowedInstructors*/) {
  return {
    restrict: 'E',
    replace: true,
    template: '<div class="instructor-follow-button"><button ng-click="followInstructor()" class="btn btn-info btn-xs">Follow</button></div>',
    scope: {
      instructorToFollow: '='
    },
    require: '^scInstructors',
    link: function(scope, element, attrs, ctrl) { // Added the last param "ctrl" because we include the parent directive with "require: '^scInstructors'"
      scope.followInstructor = function() {
        ctrl.followInstructor(scope.instructorToFollow); 
        //the function "followInstructor" is in "scInstructorsCtrl"; 
        //the param "scope.instructor" is defined in " instructor: '=' "
        element.css('display', 'none');
      }
    }
  }
});

angular.module('app').controller('scheduleCtrl',function($scope) {
  $scope.instructorList = [
    {id: 1, name: 'Professor Snape'},
    {id: 2, name: 'Provessor McGonagall'},
    {id: 3, name: 'Professor Dumbledore'}
  ]
});

Sample directive 3 (Real case usage)


var app = angular.module('app', []);

app.value('scFollowedInstructors', []);


app.controller('scInstructorsCtrl', function($scope, scFollowedInstructors) {
  $scope.scFollowedInstructors = scFollowedInstructors;
});

app.directive('scInstructors', function() {
  return {
    restrict: 'E',
    replace: true,
    templateUrl: 'scInstructors.html',
    controller: 'scInstructorsCtrl'
  }
});

app.directive('scFollowInstructor', function(scFollowedInstructors) {
  return {
    restrict: 'E',
    replace: true,
    templateUrl: 'scFollowInstructor.html',
    scope: {
      instructorToFollow: '='
    },
    link: function(scope, element, attrs, ctrl) {
      scope.followed = function() {
        return scFollowedInstructors.indexOf(scope.instructorToFollow) > -1;
      }
      scope.followInstructor = function() {
        scFollowedInstructors.push(scope.instructorToFollow);
      }
      scope.unFollowInstructor = function() {
        scFollowedInstructors.splice(scFollowedInstructors.indexOf(scope.instructorToFollow), 1);
      }
    }
  }
});

angular.module('app').controller('scheduleCtrl',function($scope) {
  $scope.instructorList = [
    {id: 1, name: 'Professor Snape'},
    {id: 2, name: 'Provessor McGonagall'},
    {id: 3, name: 'Professor Dumbledore'}
  ]
});

instructors

<div class="well sidebar-nav">
  <h3>Instructors ({{scFollowedInstructors.length}} followed)</h3>
  <div class="row" ng-repeat="instructor in instructorList">
    <div class="col-md-6">{{instructor.name}}</div>
    <div class="col-md-6"><sc-follow-instructor instructor-to-follow="instructor" /></div>
  </div>
</div>

scFollowInstructor.html

<div class="instructor-follow-button">
  <button ng-show="followed()" ng-click="unFollowInstructor()" class="btn btn-info btn-xs">Unfollow</button>
  <button ng-hide="followed()" ng-click="followInstructor()" class="btn btn-info btn-xs">Follow</button>
</div>