Can one AngularJS controller call another?

2018-12-31 03:10发布

Is it possible to have one controller use another?

For example:

This HTML document simply prints a message delivered by the MessageCtrl controller in the messageCtrl.js file.

<html xmlns:ng="http://angularjs.org/">
<head>
    <meta charset="utf-8" />
    <title>Inter Controller Communication</title>
</head>
<body>
    <div ng:controller="MessageCtrl">
        <p>{{message}}</p>
    </div>

    <!-- Angular Scripts -->
    <script src="http://code.angularjs.org/angular-0.9.19.js" ng:autobind></script>
    <script src="js/messageCtrl.js" type="text/javascript"></script>
</body>
</html>

The controller file contains the following code:

function MessageCtrl()
{
    this.message = function() { 
        return "The current date is: " + new Date().toString(); 
    };
}

Which simply prints the current date;

If I were to add another controller, DateCtrl which handed the date in a specific format back to MessageCtrl, how would one go about doing this? The DI framework seems to be concerned with XmlHttpRequests and accessing services.

13条回答
泛滥B
2楼-- · 2018-12-31 03:36

If you want to call one controller into another there are four methods available

  1. $rootScope.$emit() and $rootScope.$broadcast()
  2. If Second controller is child ,you can use Parent child communication .
  3. Use Services
  4. Kind of hack - with the help of angular.element()

1. $rootScope.$emit() and $rootScope.$broadcast()

Controller and its scope can get destroyed, but the $rootScope remains across the application, that's why we are taking $rootScope because $rootScope is parent of all scopes .

If you are performing communication from parent to child and even child wants to communicate with its siblings, you can use $broadcast

If you are performing communication from child to parent ,no siblings invovled then you can use $rootScope.$emit

HTML

<body ng-app="myApp">
    <div ng-controller="ParentCtrl" class="ng-scope">
      // ParentCtrl
      <div ng-controller="Sibling1" class="ng-scope">
        // Sibling first controller
      </div>
      <div ng-controller="Sibling2" class="ng-scope">
        // Sibling Second controller
        <div ng-controller="Child" class="ng-scope">
          // Child controller
        </div>
      </div>
    </div>
</body>

Angularjs Code

 var app =  angular.module('myApp',[]);//We will use it throughout the example 
    app.controller('Child', function($rootScope) {
      $rootScope.$emit('childEmit', 'Child calling parent');
      $rootScope.$broadcast('siblingAndParent');
    });

app.controller('Sibling1', function($rootScope) {
  $rootScope.$on('childEmit', function(event, data) {
    console.log(data + ' Inside Sibling one');
  });
  $rootScope.$on('siblingAndParent', function(event, data) {
    console.log('broadcast from child in parent');
  });
});

app.controller('Sibling2', function($rootScope) {
  $rootScope.$on('childEmit', function(event, data) {
    console.log(data + ' Inside Sibling two');
  });
  $rootScope.$on('siblingAndParent', function(event, data) {
    console.log('broadcast from child in parent');
  });
});

app.controller('ParentCtrl', function($rootScope) {
  $rootScope.$on('childEmit', function(event, data) {
    console.log(data + ' Inside parent controller');
  });
  $rootScope.$on('siblingAndParent', function(event, data) {
    console.log('broadcast from child in parent');
  });
});

In above code console of $emit 'childEmit' will not call inside child siblings and It will call inside only parent, where $broadcast get called inside siblings and parent as well.This is the place where performance come into a action.$emit is preferrable, if you are using child to parent communication because it skips some dirty checks.

2. If Second controller is child, you can use Child Parent communication

Its one of the best method, If you want to do child parent communication where child wants to communicate with immediate parent then it would not need any kind $broadcast or $emit but if you want to do communication from parent to child then you have to use either service or $broadcast

For example HTML:-

<div ng-controller="ParentCtrl">
 <div ng-controller="ChildCtrl">
 </div>
</div>

Angularjs

 app.controller('ParentCtrl', function($scope) {
   $scope.value='Its parent';
      });
  app.controller('ChildCtrl', function($scope) {
   console.log($scope.value);
  });

Whenever you are using child to parent communication, Angularjs will search for a variable inside child, If it is not present inside then it will choose to see the values inside parent controller.

3.Use Services

AngularJS supports the concepts of "Seperation of Concerns" using services architecture. Services are javascript functions and are responsible to do a specific tasks only.This makes them an individual entity which is maintainable and testable.Services used to inject using Dependency Injection mecahnism of Angularjs.

Angularjs code:

app.service('communicate',function(){
  this.communicateValue='Hello';
});

app.controller('ParentCtrl',function(communicate){//Dependency Injection
  console.log(communicate.communicateValue+" Parent World");
});

app.controller('ChildCtrl',function(communicate){//Dependency Injection
  console.log(communicate.communicateValue+" Child World");
});

It will give output Hello Child World and Hello Parent World . According to Angular docs of services Singletons – Each component dependent on a service gets a reference to the single instance generated by the service factory.

4.Kind of hack - with the help of angular.element()

This method gets scope() from the element by its Id / unique class.angular.element() method returns element and scope() gives $scope variable of another variable using $scope variable of one controller inside another is not a good practice.

HTML:-

<div id='parent' ng-controller='ParentCtrl'>{{varParent}}
 <span ng-click='getValueFromChild()'>Click to get ValueFormChild</span>
 <div id='child' ng-controller='childCtrl'>{{varChild}}
   <span ng-click='getValueFromParent()'>Click to get ValueFormParent </span>
 </div>
</div>

Angularjs:-

app.controller('ParentCtrl',function($scope){
 $scope.varParent="Hello Parent";
  $scope.getValueFromChild=function(){
  var childScope=angular.element('#child').scope();
  console.log(childScope.varChild);
  }
});

app.controller('ChildCtrl',function($scope){
 $scope.varChild="Hello Child";
  $scope.getValueFromParent=function(){
  var parentScope=angular.element('#parent').scope();
  console.log(parentScope.varParent);
  }
}); 

In above code controllers are showing their own value on Html and when you will click on text you will get values in console accordingly.If you click on parent controllers span, browser will console value of child and viceversa.

查看更多
其实,你不懂
3楼-- · 2018-12-31 03:38

Following is a publish-subscribe approach that is irrespective of Angular JS.

Search Param Controller

//Note: Multiple entities publish the same event
regionButtonClicked: function () 
{
        EM.fireEvent('onSearchParamSelectedEvent', 'region');
},

plantButtonClicked: function () 
{
        EM.fireEvent('onSearchParamSelectedEvent', 'plant');
},

Search Choices Controller

//Note: It subscribes for the 'onSearchParamSelectedEvent' published by the Search Param Controller
localSubscribe: function () {
        EM.on('onSearchParamSelectedEvent', this.loadChoicesView, this);

});


loadChoicesView: function (e) {

        //Get the entity name from eData attribute which was set in the event manager
        var entity = $(e.target).attr('eData');

        console.log(entity);

        currentSelectedEntity = entity;
        if (entity == 'region') {
            $('.getvalue').hide();
            this.loadRegionsView();
            this.collapseEntities();
        }
        else if (entity == 'plant') {
            $('.getvalue').hide();
            this.loadPlantsView();
            this.collapseEntities();
        }


});

Event Manager

myBase.EventManager = {

    eventArray:new Array(),


    on: function(event, handler, exchangeId) {
        var idArray;
        if (this.eventArray[event] == null) {
            idArray = new Array();
        } else { 
            idArray = this.eventArray[event];
        }
        idArray.push(exchangeId);
        this.eventArray[event] = idArray;

        //Binding using jQuery
        $(exchangeId).bind(event, handler);
    },

    un: function(event, handler, exchangeId) {

        if (this.eventArray[event] != null) {
            var idArray = this.eventArray[event];
            idArray.pop(exchangeId);
            this.eventArray[event] = idArray;

            $(exchangeId).unbind(event, handler);
        }
    },

    fireEvent: function(event, info) {
        var ids = this.eventArray[event];

        for (idindex = 0; idindex < ids.length; idindex++) {
            if (ids[idindex]) {

                //Add attribute eData
                $(ids[idindex]).attr('eData', info);
                $(ids[idindex]).trigger(event);
            }
        }
    }
};

Global

var EM = myBase.EventManager;
查看更多
旧时光的记忆
4楼-- · 2018-12-31 03:39

Here is a one-page example of two controllers sharing service data:

<!doctype html>
<html ng-app="project">
<head>
    <title>Angular: Service example</title>
    <script src="http://code.angularjs.org/angular-1.0.1.js"></script>
    <script>
var projectModule = angular.module('project',[]);

projectModule.factory('theService', function() {  
    return {
        thing : {
            x : 100
        }
    };
});

function FirstCtrl($scope, theService) {
    $scope.thing = theService.thing;
    $scope.name = "First Controller";
}

function SecondCtrl($scope, theService) {   
    $scope.someThing = theService.thing; 
    $scope.name = "Second Controller!";
}
    </script>
</head>
<body>  
    <div ng-controller="FirstCtrl">
        <h2>{{name}}</h2>
        <input ng-model="thing.x"/>         
    </div>

    <div ng-controller="SecondCtrl">
        <h2>{{name}}</h2>
        <input ng-model="someThing.x"/>             
    </div>
</body>
</html>

Also here: https://gist.github.com/3595424

查看更多
人气声优
5楼-- · 2018-12-31 03:39

Actually using emit and broadcast is inefficient because the event bubbles up and down the scope hierarchy which can easily degrade into performance bottlement for a complex application.

I would suggest using a service. Here is how I recently implemented it in one of my projects - https://gist.github.com/3384419.

Basic idea - register a pub-sub/event bus as a service. Then inject that event bus where ever you need to subscribe or publish events/topics.

查看更多
明月照影归
6楼-- · 2018-12-31 03:39

I also know of this way.

angular.element($('#__userProfile')).scope().close();

But I don't use it too much, because I don't like to use jQuery selectors in angular code.

查看更多
不再属于我。
7楼-- · 2018-12-31 03:39

In angular 1.5 this can be accomplished by doing the following:

(function() {
  'use strict';

  angular
    .module('app')
    .component('parentComponent',{
      bindings: {},
      templateUrl: '/templates/products/product.html',
      controller: 'ProductCtrl as vm'
    });

  angular
    .module('app')
    .controller('ProductCtrl', ProductCtrl);

  function ProductCtrl() {
    var vm = this;
    vm.openAccordion = false;

    // Capture stuff from each of the product forms
    vm.productForms = [{}];

    vm.addNewForm = function() {
      vm.productForms.push({});
    }
  }

}());

This is the parent component. In this I have created a function that pushes another object into my productForms array - note - this is just my example, this function can be anything really.

Now we can create another component that will make use of require:

(function() {
  'use strict';

  angular
    .module('app')
    .component('childComponent', {
      bindings: {},
      require: {
        parent: '^parentComponent'
      },
      templateUrl: '/templates/products/product-form.html',
      controller: 'ProductFormCtrl as vm'
    });

  angular
    .module('app')
    .controller('ProductFormCtrl', ProductFormCtrl);

  function ProductFormCtrl() {
    var vm = this;

    // Initialization - make use of the parent controllers function
    vm.$onInit = function() {
      vm.addNewForm = vm.parent.addNewForm;
    };  
  }

}());

Here the child component is creating a reference to the parents component function addNewForm which can then be bound to the HTML and called like any other function.

查看更多
登录 后发表回答