2013-09-26 3 views
80

Как вызвать метод, определенный в области дочерних объектов, из его родительской области?angularJS: Как вызвать функцию дочерней области видимости в родительской области

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

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

http://jsfiddle.net/wUPdW/

+2

Лучшая ставка, определить услугу, ввести эту услугу в оба контроллера. Или используйте корнеплод. – tymeJV

ответ

126

Вы можете использовать $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/

UPDATE: Там будет другая версия, менее связанной и более тестируемым:

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/

+0

Это круто! Но, если вы не хотите знать, где область вызова (я имею в виду в '$ scope. $ Parent' или в' $ scope. $ Parent. $ Parent' и т. Д.)? Ах, да: передайте обратный вызов в параметрах! :) – user2173353

+0

@ user2173353 вы очень правы; есть еще один способ: '$ emit' от ребенка до родителя. я думаю, что настало время обновить мой ответ. – Cherniv

+0

Приятно ли вам звонить глобальному слушателю, где где-то, дочерний контроллер в этом случае? Разве это не антипаттерн и трудно проверить позже? Разве мы не должны использовать инъекции или что-то подобное? – calmbird

27

Позвольте мне предложить другое решение:

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


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

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

Меньше кода и использования прототипичный наследования.

Plunk

+0

Может ли кто-нибудь объяснить, насколько этот подход лучше, чем упомянутый выше подход, основанный на событиях, в каких случаях и если да, то почему? Что делать, если у вас есть несколько контроллеров lvl-иерархии w? будет ли это работать, если obj.get определен внутри дочернего контроллера в дочернем контроллере, если ни один из контроллеров не имеет одного и того же имени функции? Заранее спасибо. – ZvKa

+0

Интересно ... спасибо за комментарий, что меня отбрасывает, так это то, что я думал о наследовании наследования прототипа/угловой js только применительно только к одному направлению ... child -> родительская или дочерняя область -> родительская область, а не наоборот вокруг .... но я думаю, что я что-то пропустил @ _ @ – ZvKa

+1

Позвольте мне исправить то, что я сказал, что не совсем верно. Вы правы в том, что вы сказали: Наследование области применяется только в одном направлении .... ребенок -> родитель. Что на самом деле происходит здесь, так это то, что если вы определяете $ scope.get в области child, «get» будет определяться только в области child (иногда называемый примитивным определением). Но когда вы определяете $ scope.obj.get, тогда область child будет искать obj, которая определена в родительской области, которая находится вверх по иерархии. – Canttouchit

9

Регистрация функции ребенка на родителей, когда ребенок инициализируется. Я использовал «как» обозначение для ясности в шаблоне.

ШАБЛОН

<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-initisn't supposed to be used this way!". Ну, да, но

  1. , что документация не объясняет, почему нет, и
  2. Я не верю, что авторы документации рассмотрены все возможные варианты использования для него.

Я говорю, что это хорошо для этого. Если вы хотите сфокусировать меня, прокомментируйте причины! :)

Мне нравится этот подход, потому что он сохраняет компоненты более модульными. Единственные привязки в шаблоне, и означает, что

  • ребенка контроллер не должен ничего знать о том, какой именно объект, чтобы добавить свою функцию (как в ответ @ canttouchit в)
  • родительский контроль может быть использовать с любым другим дочернего элемента управления, который имеет функцию ПОЛУЧИТЬ
  • не требует вещания, который получит очень некрасиво в большом приложении, если вы плотно не контролировать пространство имен событий

Этот подход более близко подходит Tero's idea of modularising with directives (примечание что в его модуле ed, contestants передается от родительской к «дочерней» директиве В ШАБЛОНЕ).

Действительно, другим решением может быть рассмотреть вопрос об использовании ChildCntl в качестве директивы и использовать привязку & для регистрации метода init.

+1

Я знаю, что это старый пост, но это кажется плохой идеей, потому что родитель может сохранить ссылку на ребенка после уничтожения его. –

Смежные вопросы