web-dev-qa-db-ja.com

AngularJSコンポーネントでの親から子へのイベントの通信

私が取り組んでいる新しいプロジェクトでは、ディレクティブの代わりにコンポーネントの使用を開始しました。

ただし、具体的な標準的な方法を見つけることができないという問題が発生しました。

子から親にイベントを通知するのは簡単です。以下のプランカーで見つけることができますが、親から子にイベントを通知する正しい方法は何ですか?

Angular2は、次のようなものを使用してこの問題を解決しているようです: https://angular.io/docs/ts/latest/cookbook/component-communication.html#!#parent-to-child-local-var しかし、例では#timerで行ったように、子コンポーネントへの「ポインター」を定義する可能性はありません。

Angular2への可能な簡単な変換を維持するために、私は避けたい:

  • イベント発信(スコープからの発信とブロードキャスト)
  • 子からrequireを使用します(そして、親にコールバックを追加します。UGLY)
  • 一方向バインディングを使用して、子にスコープを挿入し、このプロパティを「監視」します。

サンプルコード:

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

ここに例を見つけることができます:

http://plnkr.co/edit/SCK8XlYoYCRceCP7q2Rn?p=preview

これは私が作成した可能なソリューションです

http://plnkr.co/edit/OfANmt4zLyPG2SZyVNLr?p=preview

子は親を必要とし、その後、子は親への参照を子に設定します...今、親は子を使用できます... ugいですが、上のangular2の例のようです

21
Luca Trazzi

AngularJSコンポーネントでの親から子へのイベントの通信

式バインディングを使用してディレクティブ$ APIを公開する

親コンポーネントがイベントを子コンポーネントと通信できるようにするには、子に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++;
    }
  }
});
_

上記の例は、APIが_grid-on-init_として公開されている_$API_属性によって定義されたAngular式を呼び出します。このアプローチの利点は、親が子の初期化に反応できることです。 Angular式を使用して子コンポーネントに関数を渡すことにより。

ドキュメントから:

「分離」スコープオブジェクトハッシュは、ディレクティブの要素の属性から派生したローカルスコーププロパティのセットを定義します。これらのローカルプロパティは、テンプレートの値のエイリアスに役立ちます。オブジェクトハッシュのキーは、分離スコープのプロパティの名前にマップされます。値は、ディレクティブの要素の属性を照合することにより、プロパティが親スコープにバインドされる方法を定義します。

  • _&_または_&attr_-親スコープのコンテキストで式を実行する方法を提供します。属性名が指定されていない場合、属性名はローカル名と同じであると想定されます。 _<my-component my-attr="count = count + value">_および分離スコープ定義スコープ:_{ localFn:'&myAttr' }_を指定すると、分離スコーププロパティlocalFnは_count = count + value expression_の関数ラッパーを指します。孤立したスコープから式を介して親スコープにデータを渡すことが望ましい場合がよくあります。これは、ローカル変数名と値のマップを式ラッパーfnに渡すことで実行できます。たとえば、式がincrement($amount)である場合、localFnlocalFn({$amount: 22})として呼び出すことにより、金額の値を指定できます。

- AngularJS包括的なディレクティブAPI-スコープ

慣例として、ローカル変数を_$_でプレフィックスして親変数と区別することをお勧めします。


代わりに双方向バインディングを使用する

注:Angular 2+への移行を容易にするために、双方向_=_の使用を避けてください代わりに、一方向の_<_バインディングと式_&_バインディングを使用します。詳細については、 AngularJS開発者ガイド-コンポーネントの理解 を参照してください。

親コンポーネントがイベントを子コンポーネントと通信できるようにするには、子にAPIを公開させます。

_<grid-component api="$ctrl.gridApi"></grid-component>
_

上記の例では、_grid-component_はバインディングを使用して、api属性を使用して親スコープに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++;
    }
  }
});
_

次に、公開されたAPIを使用して、親コンポーネントが子save関数を呼び出すことができます。

_ctrl.click = function(){
  console.log("Search clicked");
  ctrl.gridApi.save();
}
_

PLNKRのデモ

30
georgeawg

簡単な方法を次に示します。 http://morrisdev.com/2017/03/triggering-events-in-a-child-component-in-angular/

基本的に、「コマンド」と呼ばれるバインドされた変数(または必要なもの)を追加し、$ onChangesを使用してその変数の変更に注意を払い、手動でトリガーするイベントをトリガーします。

私は個人的にすべての変数を「設定」と呼ばれるオブジェクトに入れ、それをすべてのコンポーネントに送信することを好みます。ただし、オブジェクト内の値を変更しても$ onChangesイベントはトリガーされないため、フラット変数を使用してイベントをトリガーするように通知する必要があります。

私はそれがそれを行うための「適切な」方法ではないと思いますが、それは確かにプログラムするのがはるかに簡単で、理解するのがはるかに簡単であり、将来的にA2に変換するのがはるかに簡単です。

4
Daniel Morris

私は同じ質問に直面しました。このアプローチについてどう思いますか:双方向バインディングの代わりにrequireを介して継承を使用しますか?

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

または、セッターメソッドを定義して、親をより明確にすることができます。

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++;
    }
  }
});
1
ViES