Komunikowanie zdarzeń z rodzica na dziecko w komponentach AngularJS

W nowym projekcie, nad którym pracuję, zacząłem używać komponentów zamiast dyrektyw.

Jednak napotkałem problem, w którym nie mogę znaleźć konkretnego standardowego sposobu, aby to zrobić.

Łatwo jest powiadamiać o zdarzeniu od dziecka do rodzica, możesz go znaleźć na moim plunkr poniżej, ale jaki jest prawidłowy sposób powiadamiania o zdarzeniu od rodzica do dziecka?

Angular2 wydaje się rozwiązywać ten problem używając czegoś takiego: https://angular.io/docs/ts/latest/cookbook/component-communication.html#!#parent-to-child-local-var Ale nie sądzę, że istnieje możliwość zdefiniowania "wskaźnika" dla komponentu podrzędnego, jak w przykładzie z # timer

W celu uzyskania możliwej łatwej konwersji na Angular2 chcę uniknąć:

  • emitowanie zdarzeń (emitowanie i nadawanie z zakresów)
  • użycie wymaganego od dziecka (a następnie dodanie wywołania zwrotnego do rodzica..Brzydki)
  • za pomocą Wiązanie jednokierunkowe, wstrzyknięcie zakresu w dziecko, a następnie "obserwuj" tę właściwość.. BARDZIEJ BRZYDKIE

Przykładowy kod:

var app = angular.module('plunker', []);

app.controller('RootController', function() {
});

app.component('parentComponent', {
  template: `
    <h3>Parent component</h3>
    <a class="btn btn-default btn-sm" ng-click="$ctrl.click()">Notify Child</a>
    <span data-ng-bind="$ctrl.childMessage"></span>
    <child-component on-change="$ctrl.notifiedFromChild(count)"></child-component>
  `,
  controller: function() {
    var ctrl = this;
    ctrl.notifiedFromChild = function(count){
      ctrl.childMessage = "From child " + count;
    }
    ctrl.click = function(){
    }
  },
  bindings: {
  }
});

app.component('childComponent', {
  template: `
    <h4>Child component</h4>
    <a class="btn btn-default btn-sm" ng-click="$ctrl.click()">Notify Parent</a>
  `,
  controller: function() {
    var ctrl = this;
    ctrl.counter = 0;
    ctrl.click = function(){
        ctrl.onChange({ count: ++ctrl.counter });
    }
  },
  bindings: {
    onChange: '&'
  }
});

Przykład można znaleźć tutaj:

Http://plnkr.co/edit/SCK8XlYoYCRceCP7q2Rn?p=preview

Jest to możliwe rozwiązanie, które stworzyłem

Http://plnkr.co/edit/OfANmt4zLyPG2SZyVNLr?p=preview

Gdzie dziecko wymaga rodzica, a następnie dziecko ustawia odniesienie rodzica do dziecka... teraz rodzic może korzystać z dziecko... brzydkie ale to jak w przykładzie angular2 powyżej

Author: georgeawg, 2016-05-25

3 answers

Komunikowanie zdarzeń z rodzica na dziecko w komponentach AngularJS

Opublikuj dyrektywę $API używając wiązania wyrażeń

Aby umożliwić komponentom nadrzędnym przekazywanie zdarzeń do komponentu podrzędnego, poproś, aby dziecko opublikowało API:

<grid-component grid-on-init="$ctrl.gridApi=$API; $ctrl.someFn($API)">
</grid-component>    

JS

app.component('gridComponent', {
  //Create API binding
  bindings: {gridOnInit: "&"},
  template: `
    <h4>Grid component</h4>
    <p> Save count = {{$ctrl.count}}</p>
  `,
  controller: function() {
    var ctrl = this;
    this.$onInit = function() {
        ctrl.count = 0;
        ctrl.api = {};
        //Publish save function
        ctrl.api.save = save;
        //Invoke Expression with $API as local
        ctrl.gridOnInit({$API: ctrl.api});
    };
    function save(){
      console.log("saved!");
      ctrl.count++;
    }
  }
});

Powyższy przykład wywołuje wyrażenie kątowe zdefiniowane przez atrybut grid-on-init z jego API wystawionym jako $API. Zaletą tego podejścia jest to, że rodzic może reagować na inicjalizację dziecka przez przekazanie funkcji komponentowi potomnemu z wyrażeniem kątowym.

From the Docs:

'isolate' Scope object hash definiuje zestaw właściwości lokalnego zakresu wywodzących się z atrybutów elementu dyrektywy. Te lokalne właściwości są przydatne do aliasowania wartości dla szablonów. Klucze w obiekcie hash mapują nazwę właściwości na izolowanym obszarze; wartości określają, w jaki sposób właściwość jest powiązana z obszarem nadrzędnym, poprzez dopasowanie atrybutów na element dyrektywy:

  • & lub &attr - zapewnia sposób wykonania wyrażenia w kontekście zakresu nadrzędnego. Jeśli nie podano nazwy attr, zakłada się, że nazwa atrybutu jest taka sama jak nazwa lokalna. Biorąc pod uwagę <my-component my-attr="count = count + value"> i definicję zakresu izolowanego scope scope: { localFn:'&myAttr' }, właściwość zakresu izolowanego localFn wskaże wrapper funkcji dla count = count + value expression. Często pożądane jest przekazywanie danych z izolowanego zakresu za pomocą wyrażenia do zakresu nadrzędnego. Można to zrobić poprzez przekazanie mapy nazw i wartości lokalnych zmiennych do opakowania wyrażenia fn. Na przykład, jeśli wyrażenie jest increment($amount), to możemy określić wartość amount przez wywołanie localFn jako localFn({$amount: 22}).

-- AngularJS Comprehensive Directive API -- scope

Jako konwencję zalecam prefiksowanie zmiennych lokalnych za pomocą $, aby odróżnić je od zmiennych nadrzędnych.


Naprzemiennie używaj wiązania dwukierunkowego

Uwaga: do ułatwiają przejście na kąt 2+, unikają stosowania wiązań dwukierunkowych =. Zamiast tego użyj wiązania jednokierunkowego < i wiązania wyrażeń &. Aby uzyskać więcej informacji, zobacz AngularJS Developer Guide-Understanding Components.

Aby umożliwić komponentom nadrzędnym przekazywanie zdarzeń do komponentu podrzędnego, poproś, aby dziecko opublikowało API:

<grid-component api="$ctrl.gridApi"></grid-component>

W powyższym przykładzie, grid-component używa wiązań do publikowania swojego API w zakresie nadrzędnym za pomocą api atrybut.

app.component('gridComponent', {
  //Create API binding
  bindings: {api: "="},
  template: `
    <h4>Grid component</h4>
    <p> Save count = {{$ctrl.count}}</p>
  `,
  controller: function() {
    var ctrl = this;
    this.$onInit = function() {
        ctrl.count = 0;
        ctrl.api = {};
        //Publish save function
        ctrl.api.save = save;
    };
    function save(){
      console.log("saved!");
      ctrl.count++;
    }
  }
});

Komponent macierzysty może wywołać funkcję potomka save za pomocą opublikowanego API:

ctrl.click = function(){
  console.log("Search clicked");
  ctrl.gridApi.save();
}

DEMO na PLNKR.

 25
Author: georgeawg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-01 15:09:23

Oto prosty sposób: http://morrisdev.com/2017/03/triggering-events-in-a-child-component-in-angular/

Zasadniczo, dodajesz powiązaną zmienną o nazwie "command" (lub cokolwiek chcesz) i używasz $onChanges, aby zwrócić uwagę na zmiany tej zmiennej i wyzwalać każde zdarzenie, które mówi, aby wyzwalać ręcznie.

Osobiście lubię umieszczać wszystkie moje zmienne w obiekcie zwanym "settings" i wysyłać je do wszystkich moich komponentów. Jednak zmiana wartości w obrębie obiekt nie uruchamia zdarzenia $onChanges, więc musisz mu powiedzieć, aby wyzwolił zdarzenie ze zmienną płaską.

Powiedziałbym, że nie jest to "właściwy" sposób, ale na pewno jest o wiele łatwiejsze do zaprogramowania, o wiele łatwiejsze do zrozumienia i o wiele łatwiejsze do konwersji na A2 później w dół drogi.

 3
Author: Daniel Morris,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-31 21:40:03

Stanąłem przed tym samym pytaniem. Co sądzisz o tym podejściu: aby użyć dziedziczenia poprzez require zamiast wiązania dwukierunkowego?

Http://plnkr.co/edit/fD1qho3eoLoEnlvMzzbw?p=preview

var app = angular.module('plunker', []);

    app.controller('RootController', function() {
    });

    app.component('filterComponent', {
      template: `
        <h3>Filter component</h3>
        <a class="btn btn-default btn-sm" ng-click="$ctrl.click()">Search</a>
        <span data-ng-bind="$ctrl.childMessage"></span>

        <grid-component api="$ctrl.gridApi"></grid-component>
      `,
      controller: function() {
        var ctrl = this;

        ctrl.click = function(){
          console.log("Search clicked");
          ctrl.gridApi.save();
        };
      }
    });

    app.component('gridComponent', {
      require: {parent:'^^filterComponent'},
      bindings: {api: "<"},
      template: `
        <h4>Grid component</h4>
        <p> Save count = {{$ctrl.count}}
      `,
      controller: function() {
        var ctrl = this;



        this.$onInit = function() {
            ctrl.count = 0;
            ctrl.api = {};
            ctrl.api.save = save;

            ctrl.parent.gridApi = ctrl.api;
        };
        function save(){
          console.log("saved!");
          ctrl.count++;
        }
      }
    });

Lub możemyzdefiniować metodę setter dla rodzica, aby uczynić ją bardziej wyraźną.

Http://plnkr.co/edit/jmETwGt32BIn3Tl0yDzY?p=preview

var app = angular.module('plunker', []);

app.controller('RootController', function() {
});

app.component('filterComponent', {
  template: `
    <h3>Filter component</h3>
    <a class="btn btn-default btn-sm" ng-click="$ctrl.click()">Search</a>
    <span data-ng-bind="$ctrl.childMessage"></span>

    <grid-component pass-api="$ctrl.setGridApi(api)"></grid-component>
  `,
  controller: function() {
    var ctrl = this;

    var gridApi = {};

    ctrl.setGridApi = function(api){
      gridApi = api;
    };

    ctrl.click = function(){
      console.log("Search clicked");
      gridApi.save();
    };
  }
});

app.component('gridComponent', {
  bindings: {
    passApi:'&'
  },
  template: `
    <h4>Grid component</h4>
    <p> Save count = {{$ctrl.count}}
  `,
  controller: function() {
    var ctrl = this;

    this.$onInit = function() {
        ctrl.count = 0;
        ctrl.api = {};
        ctrl.api.save = save;

        ctrl.passApi({api: ctrl.api});
    };
    function save(){
      console.log("saved!");
      ctrl.count++;
    }
  }
});
 0
Author: ViES,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-09 15:58:20