Royi Namir
Royi Namir

Reputation: 148634

AngularJS - Change controller by condition?

I have a simple form with data which summarize the pressed items ( via addition)

(JSBIN)

enter image description here

(700=300+400)

The form is bounded with ng-controller="OrderFormController"

Which inturn has this method :

$scope.total = function(){

        var total = 0;
        angular.forEach($scope.services, function(s){
            if (s.active){
                total+= s.price;
            }
        });

        return total;
    };

All ok.

But look at the top if the picture , there is a check box.

When it's checked , I want to do totally different calculation inside $scope.total

Let's say , instead of addition - do multiplication :

$scope.total = function(){

        var total = 1;
        angular.forEach($scope.services, function(s){
            if (s.active){
                total*= s.price;
            }
        });

        return total;
    };

Of course I could check inside the method whether the checkbox is checked , but I don't want to do that.

Question

What is the right way ( i'm angularjs beginner) of bounding it to a different method (according to checked/unchecked)?

Upvotes: 1

Views: 9456

Answers (3)

Michal Charemza
Michal Charemza

Reputation: 27022

There doesn't seem to be a built-in way to choose a controller based on some condition. The ngController directive doesn't seem to play well with interpolation of the name via {{ }}, or passing a string version of the controller name, so it doesn't seem to be possible to choose a controller by ngController dynamically based on some condition in the scope.

The most flexible solution to this I think is writing a custom directive, that accepts a string variable (including one returned by an expression)

<div dynamic-controller="checked ? 'CheckedController' : 'UncheckedController'">
  Inside {{name}}  
</div>

That then takes the name of the controller, puts it in an ng-controller attribute, and then (re)compiles the element, and do this whenever the expression returned by dynamic-controller changes. This could be done as follows:

app.directive('dynamicController', function($compile) {
  return  {
    transclude: 'element',
    scope: {
      'dynamicController': '=' 
    },
    link: function(scope, element, attr, ctrl, transclude) {
      var el = null;
      scope.$watch('dynamicController',function() {
        if (el) {
          el.remove();
          el = null;
        }
        transclude(function(clone) {
          clone.attr('ng-controller', scope.dynamicController);
          clone.removeAttr('dynamic-controller');
          el = $compile(clone[0])(scope.$parent)
          element.after(el);
        });
      });
    }
  }
});

You can see this in action in this Plunker

Upvotes: 6

Michal Charemza
Michal Charemza

Reputation: 27022

A simple way to choose controller, but keep the template the same, is to have the template in an external file, and wrap it in an ngIf, so:

<div ng-if="checked" ng-controller="CheckedController" ng-include="'template.html'">
</div>
<div ng-if="!checked" ng-controller="UncheckedController" ng-include="'template.html'">
</div>

As can be seen in this Plunker

Upvotes: 4

Stuart Nelson
Stuart Nelson

Reputation: 4202

Just delegate to different methods based on if the box is checked

$scope.total = function() {
  if($scope.isMultiply) {
    // your multiply code
  } else {
    // non-multiply code
  }
}

You don't want to check to see if the checkbox is checked, but there's no way around that. Some code somewhere is going to have to respond to the state of the checkbox. This is simple, straightforward, and easy to read.

Upvotes: 3

Related Questions