dans le nouveau projet sur lequel je travaille, j'ai commencé à utiliser les composants au lieu de directives.
cependant, j'ai rencontré un problème où je ne pouvais pas trouver de solution concrète standard.
Il est facile de notifier un événement d'enfant à parent, vous pouvez le trouver sur mon plunkr ci-dessous, mais quelle est la bonne façon de notifier un événement de parent à enfant?
Angular2 semble résoudre ce problème en utilisant quelque chose comme ceci: https://angular.io/docs/ts/latest/cookbook/component-communication.html#!#parent-to-child-local-var Mais je ne pense pas qu'il soit possible de définir un "pointeur" sur le composant enfant, comme dans l'exemple de #timer
Afin de maintenir une conversion facile possible en Angular2, je souhaite éviter:
Exemple de code:
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: '&'
}
});
Vous pouvez trouver un exemple ici:
http://plnkr.co/edit/SCK8XlyoYCRceCP7q2Rn?p=preview
C'est une solution possible que j'ai créée
http://plnkr.co/edit/OfANmt4zLyPG2SZyVNLr?p=preview
où l'enfant a besoin du parent, et ensuite l'enfant définit une référence parent à l'enfant ... maintenant le parent peut utiliser l'enfant ... moche mais c'est comme l'exemple angular2 ci-dessus
Pour permettre aux composants parents de communiquer des événements à un composant enfant, demandez à l'enfant de publier une 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++;
}
}
});
L'exemple ci-dessus appelle l'expression angulaire définie par l'attribut grid-on-init
avec son API exposée en tant que $API
. L'avantage de cette approche est que le parent peut réagir à l'initialisation de l'enfant en transmettant une fonction au composant enfant avec l'expression angulaire.
De la documentation:
Le hachage d'objet de portée 'isolate' définit un ensemble de propriétés de portée locales dérivées d'attributs de l'élément de la directive. Ces propriétés locales sont utiles pour créer des alias pour les valeurs des modèles. Les clés dans le hachage d'objet mappent le nom de la propriété sur l'étendue d'isolement; les valeurs définissent le lien entre la propriété et la portée parent, via des attributs correspondants sur l'élément de la directive:
&
ou&attr
- fournit un moyen d'exécuter une expression dans le contexte de la portée parent. Si aucun nom d'attr n'est spécifié, le nom d'attribut est supposé être le même que le nom local. Avec<my-component my-attr="count = count + value">
et l'étendue de la définition de la portée d'isolement:{ localFn:'&myAttr' }
, la propriété de portée d'isolementlocalFn
pointera vers un encapsuleur de fonctions pour lecount = count + value expression
. Il est souvent souhaitable de transmettre des données de la portée isolée via une expression à la portée parent. Cela peut être fait en transmettant une mappe de noms de variables locales et de valeurs dans le wrapper d'expression fn. Par exemple, si l'expression estincrement($amount)
, nous pouvons spécifier la valeur en appelant la variablelocalFn
sous la formelocalFn({$amount: 22})
.
- Directive complète API AngularJS - Champ d'application
Par convention, je recommande de préfixer les variables locales avec $
pour les distinguer des variables parent.
REMARQUE: Pour faciliter la transition vers Angular 2+, évitez l’utilisation de la liaison bidirectionnelle =
. Utilisez plutôt la liaison à sens unique <
et la liaison d'expression &
. Pour plus d'informations, voir AngularJS Guide de l'utilisateur - Comprendre les composants .
Pour permettre aux composants parents de communiquer des événements à un composant enfant, demandez à l'enfant de publier une API:
<grid-component api="$ctrl.gridApi"></grid-component>
Dans l'exemple ci-dessus, le grid-component
utilise des liaisons pour publier son API sur l'étendue parent à l'aide de l'attribut api
.
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++;
}
}
});
Le composant parent peut ensuite appeler la fonction enfant save
à l'aide de l'API publiée:
ctrl.click = function(){
console.log("Search clicked");
ctrl.gridApi.save();
}
La DEMO sur PLNKR .
Voici un moyen facile: http://morrisdev.com/2017/03/triggering-events-in-a-child-component-in-angular/
en gros, vous ajoutez une variable liée appelée "commande" (ou ce que vous voulez) et utilisez les $ onChanges pour faire attention aux changements de cette variable et déclencher tout événement qu'il dit déclencher manuellement.
Personnellement, j'aime bien mettre toutes mes variables dans un objet appelé "paramètres" et l'envoyer à tous mes composants. Cependant, une modification d'une valeur dans un objet NE déclenche PAS l'événement $ onChanges. Vous devez donc lui indiquer de déclencher l'événement avec une variable plate.
Je dirais que ce n'est pas la "bonne" façon de le faire, mais c'est certainement beaucoup plus facile à programmer, à comprendre, et beaucoup plus facile à convertir au format A2 plus tard.
J'ai affronté la même question. Que pensez-vous de cette approche: utiliser l’héritage via require
au lieu de la liaison bidirectionnelle?
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++;
}
}
});
Ou nous pouvons définir la méthode de définition pour le parent afin de la rendre plus explicite.
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++;
}
}
});