Dynamic NG-Controller Name

The question:

I want to dynamically specify a controller based on a config that we load. Something like this:

<div ng-controller="{{config.controllerNameString}}>
    ...
</div>

How do I do this in angular? I thought this would be very easy, but I can seem to find a way of doing this.

The Solutions:

Below are the methods you can try. The first solution is probably the best. Try others if the first one doesn’t work. Senior developers aren’t just copying/pasting – they read the methods carefully & apply them wisely to each case.

Method 1

What you want to do is have another directive run before anything else is called, get the controller name from some model remove the new directive and add the ng-controller directive, then re-compile the element.

That looks like this:

global.directive('dynamicCtrl', ['$compile', '$parse',function($compile, $parse) {
  return {
    restrict: 'A',
    terminal: true,
    priority: 100000,
    link: function(scope, elem) {
      var name = $parse(elem.attr('dynamic-ctrl'))(scope);
      elem.removeAttr('dynamic-ctrl');
      elem.attr('ng-controller', name);
      $compile(elem)(scope);
    }
  };
}]);

Then you could use it in your template, like so:

<div dynamic-ctrl="'blankCtrl'">{{tyler}}</div>

with a controller like this:

global.controller('blankCtrl',['$scope',function(tyler){
    tyler.tyler = 'tyler';
    tyler.tyler = 'chameleon';
}]);

There’s probably a way of interpolating the value ($interpolate) of the dynamic-ctrl instead of parsing it ($parse), but I couldn’t get it to work for some reason.

Method 2

I’m using it in ng-repeat, so this is improved code for loops and sub objects:

Template:

<div class="col-xs6 col-sm-5 col-md-4 col-lg-3" ng-repeat="box in boxes">
<div ng-include src="'/assets/js/view/box_campaign.html'" ng-dynamic-controller="box.type"></div>
</div>

Directive:

mainApp.directive('ngDynamicController', ['$compile', '$parse',function($compile, $parse) {
  return {
      scope: {
          name: '=ngDynamicController'
      },
      restrict: 'A',
      terminal: true,
      priority: 100000,
      link: function(scope, elem, attrs) {
          elem.attr('ng-controller', scope.name);
          elem.removeAttr('ng-dynamic-controller');

          $compile(elem)(scope);
      }
  };
}]);

Method 3

Personally the 2 current solutions here didn’t work for me, as the name of the controller would not be known when first compiling the element but later on during another digest cycle. Therefore I ended up using:

myapp.directive('dynamicController', ['$controller', function($controller) {
  return {
    restrict: 'A',
    scope: true,
    link: function(scope, elem, attrs) {
      attrs.$observe('dynamicController', function(name) {
        if (name) {
          elem.data('$Controller', $controller(name, {
            $scope: scope,
            $element: elem,
            $attrs: attrs
          }));
        }
      });
    }
  };
}]);


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Comment