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
Лучше всего, определите службу, внедрите эту службу в оба контроллера. Или используйте rootcope.
tymeJV

Ответы:


146

Вы можете использовать $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и т. Д.)? Ах да: передайте обратный вызов в params! :)
user2173353

@ user2173353 вы совершенно правы; есть еще один путь: $emitот ребенка к родителю. Думаю, пришло время обновить свой ответ ..
Иван Черных

Это хорошая идея - в этом случае вызвать глобального слушателя, где где-то находится дочерний контроллер? Разве это не антипаттерн, и его сложно протестировать позже? Разве нельзя делать уколы или что-то в этом роде?
Calmbird

@calmbird, каждую вещь нужно использовать осторожно, это точно .. Некоторые предпочитают пользоваться сервисами в подобных случаях. В любом случае, я добавил более элегантную версию (не раздражающую $parent)
Иван Черных

9
Такой подход чище. Передайте обратный вызов в, $broadcastи вы сможете полностью исключить его pingBack.
Posest

35

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

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


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

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

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

Plunk


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

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

3
Как это будет работать, если у дочернего элемента есть собственная область выделения?
Динеш

2
Вопрос не касался изолированной области, однако, если у вас есть изолированная область, вы можете использовать привязку изолированной области. Imo, вещание немного беспорядочное.
Canttouchit

3
Я не думаю, что этот пример возможен, если вам нужно получить доступ к функции дочернего контроллера из родительского контроллера , а не из шаблона. Этот пример работает только из-за двусторонней привязки к шаблону, который, как я полагаю, продолжает опрос до тех пор, пока не $scope.obj.get()станет действительной функцией.
danyim

12

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

ШАБЛОН

<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. Я не верю, что авторы документации рассмотрели ВСЕ возможные варианты его использования.

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

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

  • дочерний Контроллер не должен ничего знать о том, к какому объекту добавить свою функцию (как в ответе @ canttouchit)
  • родительский элемент управления может использоваться с любым другим дочерним элементом управления, который имеет функцию получения
  • не требует трансляции, что будет очень некрасиво в большом приложении, если вы не будете жестко контролировать пространство имен событий

Этот подход более близко подходит к идее Теро о модульности с помощью директив (обратите внимание, что в его модульном примере contestantsдиректива передается от родительской к «дочерней» В ШАБЛОНЕ).

В самом деле, другим решением может быть рассмотрение реализации ChildCntlдирективы as и использование &привязки для регистрации 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.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.