angularJS:如何在父范围中调用子范围函数


95

如何从其父范围调用在子范围中定义的方法?

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

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

http://jsfiddle.net/wUPdW/


2
最好的选择是,定义一个服务,然后将该服务注入两个控制器中。或使用rootscope。
tymeJV

Answers:


145

您可以$broadcast从父母到孩子使用:

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";    
    }
}

工作提琴:http : //jsfiddle.net/wUPdW/2/

更新:还有另一个版本,耦合度更低,更易于测试:

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";    
    }
}

小提琴:http : //jsfiddle.net/uypo360u/


太酷了!但是,如果您不(想要)知道调用方范围在哪里(我的意思是在中$scope.$parent或中$scope.$parent.$parent,等等)怎么办?嗯,是的:在参数中传递回调!:)
user2173353

@ user2173353你说的很对;还有另一种方法:$emit从孩子到父母。我认为这是更新我的回答时间..
Cherniv

在这种情况下,调用全局监听器在哪里是子控制器好吗?它是反模式的,以后很难测试吗?我们不应该使用注射剂或水雾剂吗?
镇静鸟2014年

@calmbird,每件事都必须小心使用,这是肯定的。.有些人喜欢在类似情况下使用服务。无论如何,我添加了更优雅的版本(无烦人$parent
切尔诺夫2014年

9
这种方法更清洁。在传递回调,$broadcast您可以pingBack完全消除。
2015年

34

让我提出另一个解决方案:

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


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

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

更少的代码并使用原型继承。

朋克


在某些情况下,有人可以解释这种方法是否比上面提到的事件驱动方法更好,为什么呢?如果您有多个lvl层次结构和子控制器怎么办?如果obj.get是在子控制器内的子控制器内定义的,那么只要这些控制器都没有定义相同的函数名,该方法是否可行?先感谢您。
ZvKa'2

1
让我纠正我所说的话,这不是完全正确的。您说的没错:作用域继承仅适用于一个方向... child-> parent。实际发生的是,如果您在子范围内定义$ scope.get,则“ get”将仅在子范围内定义(有时称为原始定义)。但是,当您定义$ scope.obj.get时,子作用域将查找在父作用域(在层次结构中)定义的obj。
Canttouchit 2015年

3
如果孩子有自己的隔离范围,这将如何工作?
Dinesh

2
问题不在于隔离范围,但是,如果您具有隔离范围,则可以使用隔离范围绑定。Imo,广播有点混乱。
Canttouchit 2015年

2
如果您需要从父级的控制器而不是模板访问子级控制器的功能,我认为此示例是不可能的。该示例仅由于模板上的双向绑定而起作用,我认为模板将一直轮询直到$scope.obj.get()是有效函数。
danyim

11

当孩子正在初始化时,在父母上注册孩子的功能。为了清楚起见,我在模板中使用了“ as”符号。

模板

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

控制器

...
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";    
  };
}

“但是”,您说,“ ng-init 不应该这样使用!”。好,是的,但是

  1. 该文档没有解释为什么不这样做,并且
  2. 我不认为文档作者考虑了所有可能的用例。

我说这是一个很好的用途。如果您想对我投反对票,请说明原因!:)

我喜欢这种方法,因为它使组件保持模块化。唯一的绑定在模板中,这意味着

  • 子Controller不必知道向哪个对象添加其功能(如@canttouchit的回答)
  • 父控件可以与具有get函数的任何其他子控件一起使用
  • 不需要广播,除非您严格控制事件名称空间,否则在大型应用程序中广播会非常难看

这种方法更接近于Tero使用指令进行模块化的想法(请注意,在他的模块化示例中,contestants在模板中将其从父指令传递给“子”指令)。

确实,另一种解决方案可能是考虑将ChildCntlas作为指令来实现,并使用&绑定来注册init方法。


1
我知道这是一个过时的帖子,但这似乎不是一个好主意,因为在孩子被销毁后,父母可以保留对孩子的引用。
艾米·布兰肯西

这是比广播事件更清洁的解决方案。不完美,但更好。尽管清理确实是一个问题。
mcv

-1

您可以使子对象成为对象。

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";    
    };
});

在这里孩子是get方法的证明目的地。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.