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
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 izolowanegolocalFn
wskaże wrapper funkcji dlacount = 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 jestincrement($amount)
, to możemy określić wartość amount przez wywołanielocalFn
jakolocalFn({$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();
}
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.
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++;
}
}
});
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