Comment passer des données entre les composants frères sans utiliser $scope?
Je fais un composant qui contient 3 composants enfants de cette façon:
<header-component>
<side-component>
<main-component>
Le composant principal contient la liste des héros. Le composant d'en-tête contient deux boutons qui sont supposés changer la vue sur le composant principal en vue de liste ou de grille.
Le problème que j'ai maintenant est de transmettre des données du composant d'en-tête au composant principal. Donc, quand je clique sur le bouton grille, la vue sur le contenu principal devrait changer en vue grille , de même pour la vue ligne.
Comment le des données sont transmises entre les composants enfants angulaire de 1,5 ?
3 réponses
Approche composante
Je vous suggère de vous aligner avec L'approche angulaire à 2 composants et d'utiliser les entrées /résultats approche. Si vous le faites, vous pourrez facilement migrer vers Angular 2, car les composants seront conceptuellement identiques (avec une différence seulement dans la syntaxe). Voici donc la façon dont vous le faites.
Donc, nous voulons essentiellement que header et main components partagent un morceau d'état avec header pour pouvoir le changer. Il y a plusieurs approches que nous pouvons utiliser pour le faire fonctionner, mais le plus simple est d'utiliser la propriété de contrôleur parent intermédiaire. Supposons donc que le contrôleur parent (ou le composant) définit cette propriété view
que vous souhaitez utiliser à la fois par les composants header (can read and modify) et main (can read).
Composant D'en-tête : entrée et sortie.
Voici à quoi pourrait ressembler le composant d'en-tête simple:
.component('headerComponent', {
template: `
<h3>Header component</h3>
<a ng-class="{'btn-primary': $ctrl.view === 'list'}" ng-click="$ctrl.setView('list')">List</a>
<a ng-class="{'btn-primary': $ctrl.view === 'table'}" ng-click="$ctrl.setView('table')">Table</a>
`,
controller: function() {
this.setView = function(view) {
this.view = view
this.onViewChange({$event: {view: view}})
}
},
bindings: {
view: '<',
onViewChange: '&'
}
})
La partie la plus importante ici est les liaisons. Avec view: '<'
nous spécifions que le composant header
être capable de lire quelque chose d'extérieur et de le lier en tant que propriété view
du propre contrôleur. Avec onViewChange: '&'
composants définis sorties: le canal pour notifier / mettre à jour le monde extérieur avec tout ce dont il a besoin. Le composant d'en-tête poussera certaines données à travers ce canal, mais il ne sait pas ce que le composant parent fera avec, et il ne devrait pas s'en soucier.
Ainsi que header
contrôleur peut être utilisé quelque chose comme
<header-component view="root.view" on-view-change="root.view = $event.view"></header-component>
Composant Principal: entrée.
Le composant principal est plus simple, il suffit de définir l'entrée qu'il accepte:
.component('mainComponent', {
template: `
<h4>Main component</h4>
Main view: {{ $ctrl.view }}
`,
bindings: {
view: '<'
}
})
Vue Parent
Et enfin tout est câblé ensemble:
<header-component view="root.view" on-view-change="root.view = $event.view"></header-component>
<main-component view="root.view"></main-component>
Jetez un oeil et jouer avec une démo simple.
angular.module('demo', [])
.controller('RootController', function() {
this.view = 'table'
})
.component('headerComponent', {
template: `
<h3>Header component</h3>
<a class="btn btn-default btn-sm" ng-class="{'btn-primary': $ctrl.view === 'list'}" ng-click="$ctrl.setView('list')">List</a>
<a class="btn btn-default btn-sm" ng-class="{'btn-primary': $ctrl.view === 'table'}" ng-click="$ctrl.setView('table')">Table</a>
`,
controller: function() {
this.setView = function(view) {
this.view = view
this.onViewChange({$event: {view: view}})
}
},
bindings: {
view: '<',
onViewChange: '&'
}
})
.component('mainComponent', {
template: `
<h4>Main component</h4>
Main view: {{ $ctrl.view }}
`,
bindings: {
view: '<'
}
})
<script src="https://code.angularjs.org/1.5.0/angular.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.css" />
<div class="container" ng-app="demo" ng-controller="RootController as root">
<pre>Root view: {{ root.view }}</pre>
<header-component view="root.view" on-view-change="root.view = $event.view"></header-component>
<main-component view="root.view"></main-component>
</div>
Démo: http://plnkr.co/edit/ODuY5Mp9HhbqA31G4w3t?p=info
Voici un billet de blog que j'ai écrit couvrant la conception basée sur les composants en détails: http://dfsq.info/site/read/angular-components-communication
Bien que l'approche composant parent (transmission de données via des attributs) soit une implémentation parfaite et valide, nous pouvons réaliser la même chose de manière plus simple en utilisant une usine store.
Fondamentalement, les données sont conservées par le Store
, qui est référencé dans la portée des deux composants, permettant des mises à jour réactives de l'interface utilisateur lorsque l'état change.
Exemple:
angular
.module('YourApp')
// declare the "Store" or whatever name that make sense
// for you to call it (Model, State, etc.)
.factory('Store', () => {
// hold a local copy of the state, setting its defaults
const state = {
data: {
heroes: [],
viewType: 'grid'
}
};
// expose basic getter and setter methods
return {
get() {
return state.data;
},
set(data) {
Object.assign(state.data, data);
},
};
});
Ensuite, dans vos composants, vous devriez avoir quelque chose comme:
angular
.module('YourApp')
.component('headerComponent', {
// inject the Store dependency
controller(Store) {
// get the store reference and bind it to the scope:
// now, every change made to the store data will
// automatically update your component UI
this.state = Store.get();
// ... your code
},
template: `
<div ng-show="$ctrl.state.viewType === 'grid'">...</div>
<div ng-show="$ctrl.state.viewType === 'row'">...</div>
...
`
})
.component('mainComponent', {
// same here, we need to inject the Store
controller(Store) {
// callback for the switch view button
this.switchViewType = (type) => {
// change the Store data:
// no need to notify or anything
Store.set({ viewType: type });
};
// ... your code
},
template: `
<button ng-click="$ctrl.switchViewType('grid')">Switch to grid</button>
<button ng-click="$ctrl.switchViewType('row')">Switch to row</button>
...
`
Si vous voulez voir un exemple de travail, découvrez ce CodePen.
Ce faisant, vous pouvez également activer la communication entre 2 ou n Composants . Vous n'avez qu'à:
- injecter la dépendance du magasin
- assurez-vous de lier les données du magasin à l'étendue de votre composant
, Comme dans l'exemple ci-dessus (<header-component>
).
Dans le monde réel, une application typique doit gérer beaucoup de données afin de faire plus de sens pour logiquement divisé les domaines de données d'une certaine façon. En suivant la même approche , Vous pouvez ajouter d'autres usines de magasins . Par exemple, pour gérer les informations de l'utilisateur enregistré en cours plus une ressource externe (C.-À-catalogue), vous pouvez créer un UserStore
plus un CatalogStore
-- alternativement UserModel
et CatalogModel
; ces entités seraient également de bons endroits pour centraliser des choses comme la communication avec le back-end, ajouter une logique métier personnalisée, etc. . La gestion des données sera alors sous la seule responsabilité du Store
usine.
Gardez à l'esprit que nous mutons les données du magasin. Bien que cette approche soit simple et claire, elle pourrait ne pas bien évoluer car elle produira des effets secondaires . Si vous voulez quelque chose de plus avancé (immuabilité, fonctions pures, arbre d'état unique, etc.) découvrez Redux, ou si finalement vous souhaitez basculer Angulaire 2 regardez ngrx/store.
Espérons que cela aide! :)
Vous n'avez pas à le faire L'angulaire 2 façon, parce que juste au cas où vous migriez parfois... Faire si il un sens pour vous de le faire.
Utilisez des événements personnalisés pour y parvenir.
vous pouvez transmettre un message à travers votre application à l'aide des répartiteurs d'événements $emit(name, args); or $broadcast(name, args);
Et vous pouvez écouter ces événements en utilisant la méthode $on(name, listener);
J'espère que ça aide
Réf: https://docs.angularjs.org/api/ng/type/ $ rootScope. Scope# $ emit
Exemple: vous pouvez notifier le changement comme ci-dessous à partir de votre composant d'en-tête
$rootScope.$emit("menu-changed", "list");
Et vous pouvez écouter le changement dans votre directive main-component comme
$rootScope.$on("menu-changed", function(evt, arg){
console.log(arg);
});