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
Angular2似乎用这样的方法解决了这个问题:https://angular.io/docs/ts/latest/cookbook/componentcommunication/componentcommunicationhtml #!#parent-to-child-local-var,但我不认为有可能像#timer那样定义子组件的“指针”
In order to mantain a possible easy conversion to Angular2 I want to avoid:
为了让一个容易的转换成为Angular2,我要避免:
- event emitting (emit and broadcast from the scopes)
- 事件发送(从作用域发出和广播)
- using the require from the child (and then add a callback to the parent..UGLY)
- 使用子节点的require(然后向父节点添加回调..UGLY)
- using a one-way binding, injecting the scope in the child and then "watch" this property.. MORE UGLY
- 使用单向绑定,在子对象中注入范围,然后“监视”这个属性。更丑
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
http://plnkr.co/edit/SCK8XlYoYCRceCP7q2Rn?p=preview
This is a possible solution I created
这是我创建的一个可能的解决方案
http://plnkr.co/edit/OfANmt4zLyPG2SZyVNLr?p=preview
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
子节点需要父节点,然后子节点设置父节点对子节点的引用……现在父母可以用孩子……很丑,但就像上面的angular2例子
3 个解决方案
#1
24
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:
为了允许父组件向子组件通信事件,请让子组件发布一个API:
<grid-component grid-on-init="$ctrl.gridApi=$API; $ctrl.someFn($API)">
</grid-component>
JS
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.
上面的示例调用由grid-on-init属性定义的角表达式,其API公开为$API。这种方法的优点是,父类可以通过向子组件传递一个具有角度表达式的函数来对子初始化做出反应。
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})
.- &或&attr -提供了在父范围上下文中执行表达式的方法。如果没有指定attr名称,则假定属性名称与本地名称相同。给定
和隔离范围定义范围:{localFn:'&myAttr'},隔离范围属性localFn将指向一个函数包装器,用于count = count + value表达式。通常希望通过表达式将数据从隔离范围传递到父范围。这可以通过向表达式包装器fn传递一个本地变量名和值的映射来实现。例如,如果表达式是increment($amount),那么我们可以通过调用localFn作为localFn({$amount: 22})来指定amount值。
-- AngularJS Comprehensive Directive API -- scope
——AngularJS综合指令API——范围
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.
注意:为方便过渡到角2+,避免使用双向=绑定。相反,使用单向的 <绑定和表达式&绑定。有关更多信息,请参阅angularjs开发人员指南——理解组件。< p>
To allow parent components to communicate events to a child component, have the child publish an API:
为了允许父组件向子组件通信事件,请让子组件发布一个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.
在上面的示例中,网格组件使用绑定使用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++;
}
}
});
Then the parent component can invoke the child save
function using the published API:
然后父组件可以使用已发布的API调用子保存函数:
ctrl.click = function(){
console.log("Search clicked");
ctrl.gridApi.save();
}
The DEMO on PLNKR.
PLNKR演示。
#2
2
Here is an easy way: http://morrisdev.com/2017/03/triggering-events-in-a-child-component-in-angular/
这里有一个简单的方法:http://morrisdev.com/2017/03/triggering-events-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.
基本上,您添加了一个名为“命令”(或任何您想要的)的绑定变量,并使用$onChanges关注该变量的更改,并触发它所说的手动触发的任何事件。
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.
我个人喜欢将所有的变量放入一个名为“settings”的对象中,并将其发送给所有的组件。但是,在对象内的值的更改不会触发$onChanges事件,因此您需要告诉它以一个平面变量触发事件。
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.
我认为这并不是一种“合适”的方法,但它确实更容易编程,更容易理解,也更容易在以后转换成A2。
#3
0
I faced with same question. What do you think about this approach: to use inheritance via require
instead of Bi-Directional Binding?
我面临着同样的问题。您如何看待这种方法:通过require而不是双向绑定来使用继承?
http://plnkr.co/edit/fD1qho3eoLoEnlvMzzbw?p=preview
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.
或者我们可以为父类定义setter方法,使其更显式。
http://plnkr.co/edit/jmETwGt32BIn3Tl0yDzY?p=preview
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
24
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:
为了允许父组件向子组件通信事件,请让子组件发布一个API:
<grid-component grid-on-init="$ctrl.gridApi=$API; $ctrl.someFn($API)">
</grid-component>
JS
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.
上面的示例调用由grid-on-init属性定义的角表达式,其API公开为$API。这种方法的优点是,父类可以通过向子组件传递一个具有角度表达式的函数来对子初始化做出反应。
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})
.- &或&attr -提供了在父范围上下文中执行表达式的方法。如果没有指定attr名称,则假定属性名称与本地名称相同。给定
和隔离范围定义范围:{localFn:'&myAttr'},隔离范围属性localFn将指向一个函数包装器,用于count = count + value表达式。通常希望通过表达式将数据从隔离范围传递到父范围。这可以通过向表达式包装器fn传递一个本地变量名和值的映射来实现。例如,如果表达式是increment($amount),那么我们可以通过调用localFn作为localFn({$amount: 22})来指定amount值。
-- AngularJS Comprehensive Directive API -- scope
——AngularJS综合指令API——范围
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.
注意:为方便过渡到角2+,避免使用双向=绑定。相反,使用单向的 <绑定和表达式&绑定。有关更多信息,请参阅angularjs开发人员指南——理解组件。< p>
To allow parent components to communicate events to a child component, have the child publish an API:
为了允许父组件向子组件通信事件,请让子组件发布一个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.
在上面的示例中,网格组件使用绑定使用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++;
}
}
});
Then the parent component can invoke the child save
function using the published API:
然后父组件可以使用已发布的API调用子保存函数:
ctrl.click = function(){
console.log("Search clicked");
ctrl.gridApi.save();
}
The DEMO on PLNKR.
PLNKR演示。
#2
2
Here is an easy way: http://morrisdev.com/2017/03/triggering-events-in-a-child-component-in-angular/
这里有一个简单的方法:http://morrisdev.com/2017/03/triggering-events-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.
基本上,您添加了一个名为“命令”(或任何您想要的)的绑定变量,并使用$onChanges关注该变量的更改,并触发它所说的手动触发的任何事件。
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.
我个人喜欢将所有的变量放入一个名为“settings”的对象中,并将其发送给所有的组件。但是,在对象内的值的更改不会触发$onChanges事件,因此您需要告诉它以一个平面变量触发事件。
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.
我认为这并不是一种“合适”的方法,但它确实更容易编程,更容易理解,也更容易在以后转换成A2。
#3
0
I faced with same question. What do you think about this approach: to use inheritance via require
instead of Bi-Directional Binding?
我面临着同样的问题。您如何看待这种方法:通过require而不是双向绑定来使用继承?
http://plnkr.co/edit/fD1qho3eoLoEnlvMzzbw?p=preview
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.
或者我们可以为父类定义setter方法,使其更显式。
http://plnkr.co/edit/jmETwGt32BIn3Tl0yDzY?p=preview
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++;
}
}
});