0

I need to dynamically change controller of one particular div by clicking some input buttons.

Why it works the first way, but doesn't work the second way if I replace one-element array by controller itself (see code below).

And how to implement such functionality in a better way?


Plnkr with one-element array (works)

index.html

<body ng-app="myApp">
<div ng-controller="MyCtrl">
    Hello, {{name}}!
    <input type="button" value="click me" ng-click="changeCtrl(0)"/>
    <input type="button" value="click me" ng-click="changeCtrl(1)"/>
    <input type="button" value="click me" ng-click="changeCtrl(2)"/>

    <div ng-repeat = "ctrl in curCtrl" ng-controller="ctrl">
        {{ blah }}
    </div>
</div>
</body>
</html>

script.js

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

myApp.controller("MyCtrl", MyCtrl);

function MyCtrl($scope) {
    $scope.name = 'Username';

    $scope.ctrls = [ctrlA, ctrlB, ctrlC];
    $scope.curCtrl = [ctrlA];

    $scope.changeCtrl = function (idx) {
        $scope.curCtrl = [$scope.ctrls[idx]];
    }
}

function ctrlA($scope) {$scope.blah = "One";}
function ctrlB($scope) {$scope.blah = "Two";}
function ctrlC($scope) {$scope.blah = "Three";}

Plnkr with controller instead (doesn't work)

index.html

<body ng-app="myApp">
<div ng-controller="MyCtrl">
    Hello, {{name}}!
    <input type="button" value="click me" ng-click="changeCtrl(0)"/>
    <input type="button" value="click me" ng-click="changeCtrl(1)"/>
    <input type="button" value="click me" ng-click="changeCtrl(2)"/>

    <div ng-controller="curCtrl">
        {{ blah }}
    </div>
</div>
</body>
</html>

script.js

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

myApp.controller("MyCtrl", MyCtrl);

function MyCtrl($scope) {
  $scope.name = 'Username';

  $scope.ctrls = [ctrlA, ctrlB, ctrlC];
  $scope.curCtrl = ctrlA;

  $scope.changeCtrl = function(idx) {
    $scope.curCtrl = $scope.ctrls[idx];
  }
}

function ctrlA($scope) {$scope.blah = "One";}
function ctrlB($scope) {$scope.blah = "Two";}
function ctrlC($scope) {$scope.blah = "Three";}
AT82
  • 71,416
  • 24
  • 140
  • 167
Lelby
  • 3
  • 1
  • 2

1 Answers1

0

It works with ng-repeat because ng-repeat destroys and re-compiles the HTML when the array reference changes. You would have to compile manually if you want the same result without an array, using the $compile service on the $element. It could be done in your controller, but a directive might be better.

You may also want to take advantage of client-side routing to accomplish this (ui-router allows nested states).

Check out these answers:

Dynamic NG-Controller Name

Dynamically assign ng-controller on runtime

Otherwise, you could use a quick hack with ng-if and $timeout:

$scope.changeCtrl = function(idx) {
    // ng-if sees null and destroys the HTML

    $scope.curCtrl = null;

    $timeout(function() { 
      // ng-if sees a new object and re-compiles the HTML

      $scope.curCtrl = $scope.ctrls[idx];
    });
}

<div ng-if="curCtrl" ng-controller="curCtrl">
    {{ blah }}
</div>
Frank Modica
  • 10,238
  • 3
  • 23
  • 39
  • Thanks a lot! I just wanted to make sure that angular doesn't watch for controller changes in both cases. I was confused by the fact that it worked with ng-repeat by one-element array, but you made it clear for me now. Thanks. –  Lelby May 23 '17 at 06:50