Angular中没有等价的$scope.emit()或$scope.broadcast()吗?
我知道EventEmitter功能,但据我所知,它只会向父HTML元素发出一个事件。
如果我需要在fx和。还是DOM根组件和嵌套了几层的元素之间?
Angular中没有等价的$scope.emit()或$scope.broadcast()吗?
我知道EventEmitter功能,但据我所知,它只会向父HTML元素发出一个事件。
如果我需要在fx和。还是DOM根组件和嵌套了几层的元素之间?
当前回答
我们实现了一个ngModelChange可观察指令,它通过你在自己的组件中实例化的事件发射器发送所有模型更改。您只需将事件发射器绑定到该指令。
参见:https://github.com/atomicbits/angular2-modelchangeobservable
在html中,绑定你的事件发射器(本例中为countryChanged):
<input [(ngModel)]="country.name"
[modelChangeObservable]="countryChanged"
placeholder="Country"
name="country" id="country"></input>
在你的typescript组件中,对EventEmitter执行一些异步操作:
import ...
import {ModelChangeObservable} from './model-change-observable.directive'
@Component({
selector: 'my-component',
directives: [ModelChangeObservable],
providers: [],
templateUrl: 'my-component.html'
})
export class MyComponent {
@Input()
country: Country
selectedCountries:Country[]
countries:Country[] = <Country[]>[]
countryChanged:EventEmitter<string> = new EventEmitter<string>()
constructor() {
this.countryChanged
.filter((text:string) => text.length > 2)
.debounceTime(300)
.subscribe((countryName:string) => {
let query = new RegExp(countryName, 'ig')
this.selectedCountries = this.countries.filter((country:Country) => {
return query.test(country.name)
})
})
}
}
其他回答
这是我的版本:
export interface IEventListenr extends OnDestroy{
ngOnDestroy(): void
}
@Injectable()
export class EventManagerService {
private listeners = {};
private subject = new EventEmitter();
private eventObserver = this.subject.asObservable();
constructor() {
this.eventObserver.subscribe(({name,args})=>{
if(this.listeners[name])
{
for(let listener of this.listeners[name])
{
listener.callback(args);
}
}
})
}
public registerEvent(eventName:string,eventListener:IEventListenr,callback:any)
{
if(!this.listeners[eventName])
this.listeners[eventName] = [];
let eventExist = false;
for(let listener of this.listeners[eventName])
{
if(listener.eventListener.constructor.name==eventListener.constructor.name)
{
eventExist = true;
break;
}
}
if(!eventExist)
{
this.listeners[eventName].push({eventListener,callback});
}
}
public unregisterEvent(eventName:string,eventListener:IEventListenr)
{
if(this.listeners[eventName])
{
for(let i = 0; i<this.listeners[eventName].length;i++)
{
if(this.listeners[eventName][i].eventListener.constructor.name==eventListener.constructor.name)
{
this.listeners[eventName].splice(i, 1);
break;
}
}
}
}
emit(name:string,...args:any[])
{
this.subject.next({name,args});
}
}
use:
export class <YOURCOMPONENT> implements IEventListener{
constructor(private eventManager: EventManagerService) {
this.eventManager.registerEvent('EVENT_NAME',this,(args:any)=>{
....
})
}
ngOnDestroy(): void {
this.eventManager.unregisterEvent('closeModal',this)
}
}
发出:
this.eventManager.emit("EVENT_NAME");
我在这里创建了一个pub-sub示例:
http://www.syntaxsuccess.com/viewarticle/pub-sub-in-angular-2.0
这个想法是使用RxJs subject来连接一个观察者和和可观察对象,作为一个通用的解决方案来发送和订阅自定义事件。在我的示例中,出于演示目的,我使用了一个customer对象
this.pubSubService.Stream.emit(customer);
this.pubSubService.Stream.subscribe(customer => this.processCustomer(customer));
这里还有一个现场演示: http://www.syntaxsuccess.com/angular-2-samples/#/demo/pub-sub
AngularJS中没有等价的$scope.emit()或$scope.broadcast()。 组件内部的EventEmitter很接近,但正如您所提到的,它只会向直接的父组件发出事件。
在Angular中,还有其他的替代方案,我将在下面解释。
@Input()绑定允许在有向对象图中连接应用程序模型(根到叶)。组件的更改检测器策略的默认行为是将所有更改从任何连接的组件传播到所有绑定的应用程序模型。
旁白:有两种类型的模型:视图模型和应用程序模型。应用程序模型通过@Input()绑定连接。视图模型只是一个绑定在组件模板中的组件属性(没有@Input()装饰)。
回答你的问题:
如果我需要在兄弟组件之间通信怎么办?
Shared Application Model: Siblings can communicate through a shared application model (just like angular 1). For example, when one sibling makes a change to a model, the other sibling that has bindings to the same model is automatically updated. Component Events: Child components can emit an event to the parent component using @Output() bindings. The parent component can handle the event, and manipulate the application model or its own view model. Changes to the Application Model are automatically propagated to all components that directly or indirectly bind to the same model. Service Events: Components can subscribe to service events. For example, two sibling components can subscribe to the same service event and respond by modifying their respective models. More on this below.
我如何在根组件和嵌套了几个层次的组件之间进行通信?
Shared Application Model: The application model can be passed from the Root component down to deeply nested sub-components through @Input() bindings. Changes to a model from any component will automatically propagate to all components that share the same model. Service Events: You can also move the EventEmitter to a shared service, which allows any component to inject the service and subscribe to the event. That way, a Root component can call a service method (typically mutating the model), which in turn emits an event. Several layers down, a grand-child component which has also injected the service and subscribed to the same event, can handle it. Any event handler that changes a shared Application Model, will automatically propagate to all components that depend on it. This is probably the closest equivalent to $scope.broadcast() from Angular 1. The next section describes this idea in more detail.
使用服务事件传播更改的可观察服务示例
下面是一个可观察服务的例子,它使用服务事件传播更改。添加TodoItem时,服务发出事件通知其组件订阅者。
export class TodoItem {
constructor(public name: string, public done: boolean) {
}
}
export class TodoService {
public itemAdded$: EventEmitter<TodoItem>;
private todoList: TodoItem[] = [];
constructor() {
this.itemAdded$ = new EventEmitter();
}
public list(): TodoItem[] {
return this.todoList;
}
public add(item: TodoItem): void {
this.todoList.push(item);
this.itemAdded$.emit(item);
}
}
下面是根组件订阅事件的方式:
export class RootComponent {
private addedItem: TodoItem;
constructor(todoService: TodoService) {
todoService.itemAdded$.subscribe(item => this.onItemAdded(item));
}
private onItemAdded(item: TodoItem): void {
// do something with added item
this.addedItem = item;
}
}
嵌套多层的子组件将以相同的方式订阅事件:
export class GrandChildComponent {
private addedItem: TodoItem;
constructor(todoService: TodoService) {
todoService.itemAdded$.subscribe(item => this.onItemAdded(item));
}
private onItemAdded(item: TodoItem): void {
// do something with added item
this.addedItem = item;
}
}
下面是调用服务来触发事件的组件(它可以驻留在组件树中的任何位置):
@Component({
selector: 'todo-list',
template: `
<ul>
<li *ngFor="#item of model"> {{ item.name }}
</li>
</ul>
<br />
Add Item <input type="text" #txt /> <button (click)="add(txt.value); txt.value='';">Add</button>
`
})
export class TriggeringComponent{
private model: TodoItem[];
constructor(private todoService: TodoService) {
this.model = todoService.list();
}
add(value: string) {
this.todoService.add(new TodoItem(value, false));
}
}
参考:Angular中的变更检测
我们实现了一个ngModelChange可观察指令,它通过你在自己的组件中实例化的事件发射器发送所有模型更改。您只需将事件发射器绑定到该指令。
参见:https://github.com/atomicbits/angular2-modelchangeobservable
在html中,绑定你的事件发射器(本例中为countryChanged):
<input [(ngModel)]="country.name"
[modelChangeObservable]="countryChanged"
placeholder="Country"
name="country" id="country"></input>
在你的typescript组件中,对EventEmitter执行一些异步操作:
import ...
import {ModelChangeObservable} from './model-change-observable.directive'
@Component({
selector: 'my-component',
directives: [ModelChangeObservable],
providers: [],
templateUrl: 'my-component.html'
})
export class MyComponent {
@Input()
country: Country
selectedCountries:Country[]
countries:Country[] = <Country[]>[]
countryChanged:EventEmitter<string> = new EventEmitter<string>()
constructor() {
this.countryChanged
.filter((text:string) => text.length > 2)
.debounceTime(300)
.subscribe((countryName:string) => {
let query = new RegExp(countryName, 'ig')
this.selectedCountries = this.countries.filter((country:Country) => {
return query.test(country.name)
})
})
}
}
服务事件:组件可以订阅服务事件。例如,两个兄弟组件可以订阅相同的服务事件,并通过修改各自的模型进行响应。下文将详细介绍。
但是要确保在销毁父组件时取消订阅。