in the new project I'm working on I've started using the components instead of directives.
however, I've encountered an issue where I cannot find a concrete standard way to do it.
It's easy to notify an event from child to parent, you can find it on my plunkr below, but what's the correct way to notify a event from parent to child?
Angular2 seems to solve this issue by using something like this: https://angular.io/docs/ts/latest/cookbook/component-communication.html#!#parent-to-child-local-var But I don't tink there's a possibilty to define a "pointer" to the child component like the example did with #timer
In order to mantain a possible easy conversion to Angular2 I want to avoid:
Example code:
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: '&'
}
});
You can find an example here:
http://plnkr.co/edit/SCK8XlYoYCRceCP7q2Rn?p=preview
This is a possible solution I created
http://plnkr.co/edit/OfANmt4zLyPG2SZyVNLr?p=preview
where the child requires the parent, and then child sets a parent reference to the child... now parent can use the child... ugly but it's like angular2 example above
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 propertylocalFn
will point to a function wrapper for thecount = 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 isincrement($amount)
then we can specify the amount value by calling thelocalFn
aslocalFn({$amount: 22})
.
-- AngularJS Comprehensive Directive API -- scope
As a convention, I recommend prefixing local variables with $
to distinguish them from parent variables.
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.