web-dev-qa-db-ja.com

$ scopeを使用せずに兄弟コンポーネント間でデータを渡す方法

この方法で3つの子コンポーネントを含むコンポーネントを作成しています。

<header-component>
<side-component>
<main-component>

メインコンポーネントにはヒーローのリストが含まれています。ヘッダーコンポーネントには、メインコンポーネントのビューをリストビューまたはグリッドビューに切り替える2つのボタンが含まれています。

私が今抱えている問題は、ヘッダーコンポーネントからメインコンポーネントにデータを渡すことです。したがって、グリッドボタンをクリックすると、メインコンテンツのビューが行ビューと同じグリッドビューに変更されます。

angular 1.5の子コンポーネント間でデータを渡すにはどうすればよいですか?

49
Beslinda N.

コンポーネントアプローチ

Angular 2コンポーネントアプローチに合わせて、 inputs / outputs アプローチを使用することをお勧めします。そうした場合、コンポーネントは概念的に同一であるため(構文のみが異なる)、Angular 2に簡単に移行できます。だからあなたはそれを行う方法です。

したがって、基本的には、ヘッダーとメインコンポーネントがヘッダーと状態を共有して、ヘッダーを変更できるようにする必要があります。動作させるために使用できる方法はいくつかありますが、最も簡単なのは、中間の親コントローラープロパティを使用することです。したがって、親コントローラー(またはコンポーネント)が、ヘッダー(読み取りおよび変更可能)およびメイン(読​​み取り可能)コンポーネントの両方で使用するこのviewプロパティを定義すると仮定しましょう。

ヘッダーコンポーネント:入力と出力。

シンプルなヘッダーコンポーネントは次のようになります。

.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: '&'
  }
})

ここで最も重要な部分はバインディングです。 view: '<'を使用して、headerコンポーネントが外部の何かを読み取り、それを自身のコントローラーのviewプロパティとしてバインドできるように指定します。 onViewChange: '&'コンポーネントで定義された出力:必要なもので外界に通知/更新するためのチャネル。ヘッダーコンポーネントは、このチャネルを介して一部のデータをプッシュしますが、親コンポーネントがそれをどのように処理するかを知らないため、気にする必要はありません。

つまり、headerコントローラーは次のように使用できることを意味します。

<header-component view="root.view" on-view-change="root.view = $event.view"></header-component> 

主成分:入力.

メインコンポーネントはよりシンプルで、受け入れる入力を定義するだけです。

.component('mainComponent', {
  template: `
    <h4>Main component</h4>
    Main view: {{ $ctrl.view }}
  `,
  bindings: {
    view: '<'
  }
})

親ビュー

そして最後に、すべてが配線されました。

<header-component view="root.view" on-view-change="root.view = $event.view"></header-component>
<main-component view="root.view"></main-component>

簡単なデモをご覧ください。

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>

デモ:http://plnkr.co/edit/ODuY5Mp9HhbqA31G4w3t?p=info


以下は、コンポーネントベースの設計を詳細にカバーしたブログ投稿です。 http://dfsq.info/site/read/angular-components-communication

83
dfsq

親コンポーネントアプローチ(属性を介してデータを引き渡す)は完全に有効でありながら優れた実装ですが、- store factory。

基本的に、データは両方のコンポーネントスコープで参照されるStoreによって保持され、状態が変化したときにUIの事後更新を可能にします。

例:

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);
            },
        };
    });

次に、コンポーネントに次のようなものが必要です。

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>
            ...
        `

実際の例をご覧になりたい場合は、 このCodePenをご覧ください です。

そうすることで、2つまたはN個のコンポーネント間の通信を有効にすることもできます。あなただけでする必要があります:

  1. ストア依存関係を注入します
  2. ストアデータをコンポーネントスコープにリンクしてください。

上記の例のように(<header-component>)。

現実の世界では、典型的なアプリケーションは大量のデータを管理する必要があるため、何らかの方法でデータドメインを論理的に分割する方が理にかなっています。同じアプローチに従って、ストアファクトリをさらに追加できます。たとえば、現在ログに記録されているユーザー情報と外部リソース(つまりカタログ)を管理するには、UserStoreCatalogStoreを構築できます-あるいは、UserModelCatalogModel; これらのエンティティは、バックエンドとの通信、カスタムビジネスロジックの追加などを集中化するのに適した場所です。データ管理は、Storeファクトリーの唯一の責任になります。

ストアデータを変更していることに注意してください。このアプローチは非常にシンプルで明確ですが、 副作用 を生成するため、うまくスケールしない可能性があります。より高度なもの(不変性、純粋な関数、単一状態ツリーなど)が必要な場合は、 Redux を確認するか、最終的にAngular 2に切り替えたい場合は、 ngrx/store

お役に立てれば! :)

念のため移行する場合があるため、Angular 2の方法で実行する必要はありません...実行する意味がある場合は実行してください。

15
Andrea Puddu

これを実現するには、カスタムイベントを使用します。イベントディスパッチャ$emit(name, args); or $broadcast(name, args);を使用してアプリケーション全体にメッセージを渡すことができます。また、メソッド$ on(name、listener)を使用してこのイベントをリッスンできます。

それが役に立てば幸い

参照: https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$emit

例:ヘッダーコンポーネントから以下のような変更を通知できます

$rootScope.$emit("menu-changed", "list");

そして、次のようにメインコンポーネントディレクティブの変更を聞くことができます

$rootScope.$on("menu-changed", function(evt, arg){
  console.log(arg);
});
6
Subash Selvaraj