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
Найкраще, визначте послугу, введіть цю послугу в обидва контролери. Або скористайтеся коренескопом.
— tymeJV

Відповіді:


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

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


Це досить круто! Але що робити, якщо ви не хочете (не хочете) знати, де знаходиться область виклику (я маю на увазі $scope.$parentабо в $scope.$parent.$parent, тощо)? А, так: передайте зворотний дзвінок у парамах! :)
— user2173353

@ user2173353 ви дуже праві; є ще один спосіб: $emitвід дитини до батька. я думаю, настав час оновити свою відповідь ..
— Чернів

Чи є гарною ідеєю зателефонувати глобальному слухачеві, де є десь, у цьому випадку дочірній контролер? Хіба це не антипатерн і важко перевірити пізніше? Чи не повинні ми використовувати ін’єкції чи щось?
— calmbird

@calmbird, кожною річчю потрібно користуватися обережно, це точно ... Деякі воліють користуватися послугами в подібних випадках. У будь-якому разі, я додав більш елегантну версію (не дратуючи $parent)
— Чернів

9
Такий підхід є більш чистим. Передайте зворотний дзвінок на $broadcastі ви можете повністю усунути його pingBack.
— Пост

34

Дозвольте запропонувати інше рішення:

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


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

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

Менше коду та використання прототипічного успадкування.

Планку


Чи може хтось пояснити, чи є такий підхід кращим за підхід, орієнтований на події, згаданий вище, в яких випадках і якщо він є, чому? що якщо у вас є багаторівнева ієрархія з дочірніми контролерами? чи буде це працювати, якщо obj.get визначений у дочірньому контролері в дочірньому контролері до тих пір, поки жоден з контролерів не має однакової назви функції? Спасибі заздалегідь.
— ZvKa

1
Дозвольте мені виправити сказане мною, що не є абсолютно правдою. Ви вірно сказали: успадкування сфери застосування застосовується лише в одному напрямку .... дитина -> батько. Насправді тут відбувається те, що якщо ви визначите $ scope.get у дочірній області, 'get' буде визначено лише в дочірній області (Іноді називається примітивним визначенням). Але коли ви визначаєте $ scope.obj.get, тоді дочірня область буде шукати obj, який визначається в батьківській області, яка відповідає ієрархії.
— Canttouchit

3
Як це буде працювати, якщо дитина має власну ізоляційну сферу?
— Дінеш

2
Питання не стосувалось ізольованої області, однак, якщо у вас є ізольована область, ви можете використовувати прив'язку ізоляції області. Імо, мовлення трохи брудне.
— Канттучіт,

2
Я не думаю, що цей приклад можливий, якщо вам потрібно отримати доступ до функції дочірнього контролера від батьківського контролера , а не шаблону. Цей приклад працює лише через двостороннє прив'язку до шаблону, яке, як я вважаю, продовжує опитувати, поки $scope.obj.get()не стане дійсною функцією.
— Данім

11

Зареєструйте функцію дитини на батьків, коли дитина ініціалізується. Я використовував позначення "як" для наочності в шаблоні.

ТЕМПЛАТ

<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)
  • батьківський контроль можна використовувати з будь-яким іншим дочірнім контролем, який має функцію get
  • не вимагає трансляції, що стане дуже потворним у великому додатку, якщо ви не будете жорстко контролювати простір імен подій

Цей підхід більш детально підходить до ідеї Теро про модуляцію з директивами (зауважте, що в його модульованому прикладі contestantsпередається ВІД ТЕМПЛАТИ від батьківської до "дочірньої" директиви).

Дійсно, іншим рішенням може бути розгляд використання ChildCntlдирективи та використання &прив'язки для реєстрації 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.