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 ?

49
demandé sur isherwood 2016-03-16 14:09:50

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

83
répondu dfsq 2016-06-29 14:56:00

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'à:

  1. injecter la dépendance du magasin
  2. 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.

15
répondu Andrea Puddu 2017-11-17 19:07:20

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);
});
6
répondu Subash Selvaraj 2016-03-16 11:25:41