Communicating Events from Parent to Child in AngularJS Components Communicating Events from Parent to Child in AngularJS Components javascript javascript

Communicating Events from Parent to Child in AngularJS Components


Communicating Events from Parent to Child in AngularJS Components

Publish Directive $API Using Expression Binding

To allow parent components to communicate events to a child component, have the child publish an 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++;    }  }});

The above example invokes the Angular Expression defined by the grid-on-init attribute with its API exposed as $API. The advantage to this approach is that the parent can react to child initialization by passing a function to the child component with the Angular Expression.

From the Docs:

The 'isolate' scope object hash defines a set of local scope properties derived from attributes on the directive's element. These local properties are useful for aliasing values for templates. The keys in the object hash map to the name of the property on the isolate scope; the values define how the property is bound to the parent scope, via matching attributes on the directive's element:

  • & or &attr - provides a way to execute an expression in the context of the parent scope. If no attr name is specified then the attribute name is assumed to be the same as the local name. Given <my-component my-attr="count = count + value"> and the isolate scope definition scope: { localFn:'&myAttr' }, the isolate scope property localFn will point to a function wrapper for the count = count + value expression. Often it's desirable to pass data from the isolated scope via an expression to the parent scope. This can be done by passing a map of local variable names and values into the expression wrapper fn. For example, if the expression is increment($amount) then we can specify the amount value by calling the localFn as localFn({$amount: 22}).

-- AngularJS Comprehensive Directive API -- scope

As a convention, I recommend prefixing local variables with $ to distinguish them from parent variables.


Alternately use Bi-Directional Binding

NOTE: To ease the transition to Angular 2+, avoid the use of bi-directional = binding. Instead use one-way < binding and expression & binding. For more information, see AngularJS Developer Guide - Understanding Components.

To allow parent components to communicate events to a child component, have the child publish an API:

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

In the above example, the grid-component uses bindings to publish its API onto the parent scope using the api attribute.

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

Then the parent component can invoke the child save function using the published API:

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

The DEMO on PLNKR.


Here is an easy way: http://morrisdev.com/2017/03/triggering-events-in-a-child-component-in-angular/

basically, you add a bound variable called "command" (or whatever you want) and use the $onChanges to pay attention to changes of that variable and trigger whatever event it says to trigger manually.

I personally like to put all my variables into an object called "settings" and send that to all my components. However, a change to a value within an object does NOT trigger the $onChanges event, so you NEED to tell it to trigger the event with a flat variable.

I'd say it is not the "proper" way to do it, but it sure is a lot easier to program, a lot easier to understand, and a lot easier to convert to A2 later on down the road.


I faced with same question. What do you think about this approach: to use inheritance via require instead of Bi-Directional Binding?

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

Or we can define setter method for parent to make it more explicit.

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