How to set the dynamic controller for directives?

24,511

Solution 1

Now it is possible with AngularJS. In directive you just add two new property called controller , name property and also isolate scope is exactly needed here.

Important to note in directive

scope:{}, //isolate scope
controller : "@", // @ symbol
name:"controllerName", // controller names property points to controller.

Working Demo for Setting Dynamic controller for Directives

HTML Markup :

<communicator controller-name="PhoneCtrl" ></communicator>
<communicator controller-name="LandlineCtrl" ></communicator>

Angular Controller and Directive :

var app = angular.module('myApp',[]).
directive('communicator', function(){
return {
    restrict : 'E',
    scope:{},
    controller : "@",
    name:"controllerName", 
    template:"<input type='text' ng-model='message'/><input type='button' value='Send Message' ng-click='sendMsg()'><br/>"          
  }   
}).
controller("PhoneCtrl",function($scope){
 $scope.sendMsg = function(){
     alert( $scope.message + " : sending message via Phone Ctrl");
    }
}).
controller("LandlineCtrl",function($scope){
    $scope.sendMsg = function(){
        alert( $scope.message + " : sending message via Land Line Ctrl ");
    }
})

Your case you can try this below code snippets.

Working Demo

HTML Markup :

<div add-icons controller-name="IconsOneCtrl">
</div>
<div add-icons controller-name="IconsTwoCtrl">
</div>

Angular Code :

angular.module('myApp',[]).

directive('addIcons', function(){
return {
    restrict : 'A',
    scope:{},
    controller : "@",
    name:"controllerName",    
    template:'<input type="button" value="(+) plus" ng-click="add()">'
  }
}).
controller("IconsOneCtrl",function($scope){
     $scope.add = function(){
        alert("IconsOne add ");
      }
}).
controller("IconsTwoCtrl",function($scope){
     $scope.add = function(){
        alert("IconsTwo add ");
      }
});

Solution 2

This is how it is done:

Inside your directive element all you need is an attribute which gives you access to the name of the controller: in my case my card attribute holds a card object which has a name property. In the directive you set the isolate scope to:

scope: { card: '=' } 

This isolates and interpolates the card object to the directive scope. You then set the directive template to:

template: '',

this looks to the directive's controller for a function named getTemplateUrl and allows you to set the templateUrl dynamically as well. In the directive controller the getTemplateUrl function looks like this:

controller: ['$scope', '$attrs', function ($scope, $attrs) { 
    $scope.getTemplateUrl = function () { return '/View/Card?cardName=' + 
        $scope.card.name; }; }],

I have an mvc controller which links up the proper .cshtml file and handles security when this route is hit, but this would work with a regular angular route as well. In the .cshtml/html file you set up your dynamic controller by simply putting as the root element. The controller will differ for each template. This creates a hierarchy of controllers which allows you to apply additional logic to all cards in general, and then specific logic to each individual card. I still have to figure out how I'm going to handle my services but this approach allows you to create a dynamic templateUrl and dynamic controller for a directive using an ng-repeat based on the controller name alone. It is a very clean way of accomplishing this functionality and it is all self-contained.

Share:
24,511
kuitos.lau
Author by

kuitos.lau

To be a geek

Updated on October 26, 2020

Comments

  • kuitos.lau
    kuitos.lau over 3 years

    Talk is cheap, show my codes first:

    HTML:

    <div add-icons="IconsCtrl">
    </div>
    

    directive:

    angular.module('attrDirective',[]).directive('addIcons', function($compile){
    return {
        restrict : 'A',
        controller : "IconsCtrl"
        },
        link : function (scope, elem , attrs, ctrl) {
            var parentElem = $(elem);
            var icons = $compile("<i class='icon-plus' ng-click='add()'></i>)(scope);
            parentElem.find(".accordion-heading").append(icons);
        },
    }
    

    });

    controller:

    function IconsCtrl($scope){
      $scope.add = function(){
        console.log("add");
      };
    }
    

    now it works, when i click the plus icon, browser console output "add".

    but i want to set the controller into the directive dynamically,like this:

    HTML:

    <div add-icons="IconsOneCtrl">
    </div>
    <div add-icons="IconsTwoCtrl">
    </div>
    

    Controller:

    function IconsOneCtrl($scope){
           $scope.add = function(){
            console.log("IconsOne add");
           };
        }
    
    function IconsTwoCtrl($scope){
        $scope.add = function(){
            console.log("IconsTwo add");
        }
    }
    

    directive likes :

    angular.module('attrDirective',[]).directive('addIcons', function($compile){
    return {
        restrict : 'A',
        controller : dynamic set,depends on attrs.addIcons
        },
        link : function (scope, elem , attrs, ctrl) {
            var parentElem = $(elem);
            var icons = $compile("<i class='icon-plus' ng-click='add()'></i>)(scope);
            parentElem.find(".accordion-heading").append(icons);
        },
    }
    });
    

    how to achieve my goal? thanks for your answer!

  • ThePuzzleMaster
    ThePuzzleMaster about 10 years
    Thanks for the thorough answer. This worked great for me, although I had to remove the isolate scope from my directive. Otherwise, worked great!
  • james
    james over 9 years
    I'm not sure the isolate scope is necessary either, i also have it working without it. Thanks!
  • Wlada
    Wlada over 9 years
    Is is possible in this case to have optional controller? Tried to pass '@?' to directive controller property but it doesn't work. Is there some other way?
  • Dante Regis
    Dante Regis about 8 years
    Hi Raja! Your solution is working here but I don't really understand why... I can't find any documentation on the 'name' property for directives, and it does not seem to work with only the 'controller' property. What does this 'name' thing do?
  • Robert Koritnik
    Robert Koritnik over 7 years
    @DanteRegis name is the directive name which is usually provided when calling angular.directive(name, fn). By setting controller: '@' we tell the directive to use controller using the value assigned to directive name (as in <div name="ControllerName">). This functionality is not documented because it's internal feature that Angular uses to implement ngController directive. It's also the only Anglar directive that actually uses this. Without this feature it would be much harder for them to implement it. The good thing is that we can be assured it will stay as part of Angular 1.x.
  • Arjun
    Arjun over 4 years
    This is great, but can i pass a variables to a controller? if yes then how?