97

How can call a method defined in child scope from its parent scope?

function ParentCntl() {
    // I want to call the $scope.get here
}

function ChildCntl($scope) {
    $scope.get = function() {
        return "LOL";    
    }
}

http://jsfiddle.net/wUPdW/

Martin
  • 3,096
  • 1
  • 26
  • 46
9blue
  • 4,693
  • 9
  • 29
  • 43

4 Answers4

148

You can use $broadcast from the parent to a child:

function ParentCntl($scope) {

    $scope.msg = "";
    $scope.get = function(){
        $scope.$broadcast ('someEvent');
        return  $scope.msg;        
    }
}

function ChildCntl($scope) {               
    $scope.$on('someEvent', function(e) {  
        $scope.$parent.msg = $scope.get();            
    });

    $scope.get = function(){
        return "LOL";    
    }
}

Working fiddle: http://jsfiddle.net/wUPdW/2/

UPDATE: There is another version, less coupled and more testable:

function ParentCntl($scope) {
    $scope.msg = "";
    $scope.get = function(){
        $scope.$broadcast ('someEvent');
        return  $scope.msg;        
    }

    $scope.$on('pingBack', function(e,data) {  
        $scope.msg = data;        
    });
}

function ChildCntl($scope) {               
    $scope.$on('someEvent', function(e) {  
        $scope.$emit("pingBack", $scope.get());        
    });

    $scope.get = function(){
        return "LOL";    
    }
}

Fiddle: http://jsfiddle.net/uypo360u/

Ivan Chernykh
  • 41,617
  • 13
  • 134
  • 146
  • That's pretty cool! But, what if you don't (want to) know where the caller scope is (I mean in `$scope.$parent` or in `$scope.$parent.$parent`, etc)? Ah, yes: pass a callback in params! :) – user2173353 Jul 28 '14 at 11:23
  • @user2173353 you're very right; there is one more way: `$emit` from a child to a parent. i think it is a time to update my answer.. – Ivan Chernykh Jul 28 '14 at 11:31
  • Is it good idea to call to global listener where is somwhere, child controller in this case? Isnt it antipattern and hard to test later? Shouldn't we use injections or smth? – Pikachu Aug 30 '14 at 12:38
  • @calmbird , each thing must be used carefully, that is for sure.. Some prefer using services in similar cases. Anyway , i added more elegant version (without annoying `$parent`) – Ivan Chernykh Aug 30 '14 at 13:54
  • You're lucky `$broadcast` and `$emit` are synchronous, else these approaches wouldn't work. There is nothing in the [documentation](https://docs.angularjs.org/api/ng/type/$rootScope.Scope) that says they are necessarily synchronous by design either – poshest May 14 '15 at 14:17
  • 10
    [This approach](http://jsfiddle.net/uypo360u/29/) is cleaner. Pass a callback at the `$broadcast` and you can eliminate the `pingBack` altogether. – poshest May 14 '15 at 14:19
  • I agree with poshest. You don't need `pingBack` nor that `$scope.msg`. You can pass also an argument object to `$broadcast`, if you don't like the idea with passing callback function. – JustAMartin Jun 18 '15 at 13:22
35

Let me suggest another solution:

var app = angular.module("myNoteApp", []);


app.controller("ParentCntl", function($scope) {
    $scope.obj = {};
});

app.controller("ChildCntl", function($scope) {
    $scope.obj.get = function() {
            return "LOL";    
    };
});

Less code and using prototypical inheritance.

Plunk

Canttouchit
  • 3,149
  • 6
  • 38
  • 51
  • Could someone explain if this approach is better than the event driven approach mentioned above in which cases and if it is, why? what if you have multi lvl hierarchy w child controllers? would this work if the obj.get is defined inside a child controller within a child controller as long as none of the controllers have the same function name defined? Thank you in advance. – ZvKa Feb 03 '15 at 18:57
  • Interesting... thanks for the comment, what's throwing me off is that i thought js prototypical / angularjs scope inheritance only applied to one direction only.... child - > parent or child scope -> parent scope, not the other way around....but i guess i'm missing something @_@ – ZvKa Feb 04 '15 at 21:58
  • 1
    Let me correct what I said which is not totally true. You are correct in what you said: scope inheritance is only applied to one direction only.... child - > parent. What actually is happening here is that if you define $scope.get in child scope, 'get' will be defined only on child scope(Sometimes referred as primitive definition). But when you define $scope.obj.get, then child scope will look for obj which is defined on parent scope, which is up the hierarchy. – Canttouchit Feb 05 '15 at 09:43
  • 3
    How will this work if child has its own isolate scope? – Dinesh Apr 15 '15 at 21:40
  • 2
    The question wasn't referring isolated scope, However if you have isolated scope you can use isolate scope binding. Imo, broadcasting is a bit messy. – Canttouchit Apr 16 '15 at 05:55
  • 3
    I don't think this example is possible if you need to access the child controller's function from the parent's _controller_, not template. This example only works because of the two-way binding on the template, which I assume keeps polling until `$scope.obj.get()` is a valid function. – danyim Mar 18 '16 at 17:17
13

Register the child's function on the parent when the child is initialising. I used "as" notation for clarity in the template.

TEMPLATE

<div ng-controller="ParentCntl as p">
  <div ng-controller="ChildCntl as c" ng-init="p.init(c.get)"></div>
</div>

CONTROLLERS

...
function ParentCntl() {
  var p = this;
  p.init = function(fnToRegister) {
    p.childGet = fnToRegister;
  };
 // call p.childGet when you want
}

function ChildCntl() {
  var c = this;
  c.get = function() {
    return "LOL";    
  };
}

"But", you say, "ng-init isn't supposed to be used this way!". Well, yes, but

  1. that documentation doesn't explain why not, and
  2. I don't believe the documentation authors considered ALL possible use cases for it.

I say this is a good use for it. If you want to downvote me, please comment with reasons! :)

I like this approach because it keeps the components more modular. The only bindings are in the template, and means that

  • the child Controller doesn't have to know anything about which object to add its function to (as in @canttouchit's answer)
  • the parent control can be used with any other child control which has a get function
  • doesn't require broadcasting, which will get very ugly in a big app unless you tightly control the event namespace

This approach more closely approaches Tero's idea of modularising with directives (note that in his modularised example, contestants is passed from parent to "child" directive IN THE TEMPLATE).

Indeed another solution might be to consider implementing the ChildCntl as a directive and use the & binding to register the init method.

poshest
  • 4,157
  • 2
  • 26
  • 37
  • 1
    I know this is an old post, but this seems like a bad idea because the parent can keep a reference to the child after the child is destroyed. – Amy Blankenship Jan 06 '16 at 18:40
  • 1
    This is a far cleaner solution than broadcasting events. Not perfect, but better. Though cleanup is indeed an issue. – mcv May 25 '18 at 12:28
-1

You can make child object.

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


app.controller("ParentCntl", function($scope) {
    $scope.child= {};
    $scope.get = function(){
      return $scope.child.get(); // you can call it. it will return 'LOL'
    }
   // or  you can call it directly like $scope.child.get() once it loaded.
});

app.controller("ChildCntl", function($scope) {
    $scope.obj.get = function() {
            return "LOL";    
    };
});

Here child is proving destination of get method.

Ramu Agrawal
  • 518
  • 6
  • 9