Angularに$scope.emit()
や$scope.broadcast()
に相当するものはありませんか?
私はEventEmitter
の機能を知っていますが、私が理解している限りでは、それは単に親HTML要素にイベントを発行するだけです。
私はfxの間で通信する必要がある場合はどうなります。兄弟、またはDOMのルートにあるコンポーネントと、いくつかのレベルのネストした要素との間の?
AngularJSの$scope.emit()
や$scope.broadcast()
に相当するものはありません。コンポーネント内のEventEmitterは近くなりますが、前述のとおり、直接の親コンポーネントにのみイベントを発行します。
Angularには他にも選択肢があります。
@Input()バインディングを使用すると、アプリケーションモデルを有向オブジェクトグラフ(ルートからリーフ)に接続できます。コンポーネントの変更検出戦略のデフォルトの動作は、接続されているコンポーネントからのすべてのバインディングについて、すべての変更をアプリケーションモデルに伝播することです。
余談:モデルには、ビューモデルとアプリケーションモデルの2種類があります。アプリケーションモデルは@Input()バインディングを通じて接続されています。ビューモデルは、コンポーネントのテンプレートにバインドされている単なるコンポーネントプロパティです(@Input()で装飾されていません)。
あなたの質問に答えるには:
兄弟コンポーネント間で通信する必要がある場合はどうなりますか?
共有アプリケーションモデル :兄弟は共有アプリケーションモデルを介して通信できます(angle 1と同じ)。たとえば、ある兄弟がモデルを変更すると、同じモデルにバインドされているもう一方の兄弟が自動的に更新されます。
コンポーネントイベント :子コンポーネントは@Output()バインディングを使用して親コンポーネントにイベントを発行できます。親コンポーネントはイベントを処理し、アプリケーションモデルまたはそれ自身のビューモデルを操作できます。アプリケーションモデルへの変更は、同じモデルに直接または間接的にバインドされているすべてのコンポーネントに自動的に伝播されます。
サービスイベント :コンポーネントはサービスイベントを購読できます。たとえば、2つの兄弟コンポーネントが同じサービスイベントを購読し、それぞれのモデルを変更して応答することができます。これについての詳細は下記をご覧ください。
Rootコンポーネントと数レベルの階層にネストしたコンポーネントとの間でどのようにやり取りできますか?
$scope.broadcast()
に最も近いものです。次のセクションでは、この概念について詳しく説明します。サービスイベントを使用して変更を伝達する監視可能なサービスの例
これは、サービスイベントを使用して変更を伝播する監視可能なサービスの例です。 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 の変更検出
Angular 2の$ scope.emit()または$ scope.broadcast()の代わりにshared serviceを使用して次のコードを処理する例イベント.
import {Injectable} from 'angular2/core';
import * as Rx from 'rxjs/Rx';
@Injectable()
export class EventsService {
constructor() {
this.listeners = {};
this.eventsSubject = new Rx.Subject();
this.events = Rx.Observable.from(this.eventsSubject);
this.events.subscribe(
({name, args}) => {
if (this.listeners[name]) {
for (let listener of this.listeners[name]) {
listener(...args);
}
}
});
}
on(name, listener) {
if (!this.listeners[name]) {
this.listeners[name] = [];
}
this.listeners[name].Push(listener);
}
off(name, listener) {
this.listeners[name] = this.listeners[name].filter(x => x != listener);
}
broadcast(name, ...args) {
this.eventsSubject.next({
name,
args
});
}
}
使用例
放送:
function handleHttpError(error) {
this.eventsService.broadcast('http-error', error);
return ( Rx.Observable.throw(error) );
}
リスナー:
import {Inject, Injectable} from "angular2/core";
import {EventsService} from './events.service';
@Injectable()
export class HttpErrorHandler {
constructor(eventsService) {
this.eventsService = eventsService;
}
static get parameters() {
return [new Inject(EventsService)];
}
init() {
this.eventsService.on('http-error', function(error) {
console.group("HttpErrorHandler");
console.log(error.status, "status code detected.");
console.dir(error);
console.groupEnd();
});
}
}
複数の引数をサポートできます。
this.eventsService.broadcast('something', "Am I a?", "Should be b", "C?");
this.eventsService.on('something', function (a, b, c) {
console.log(a, b, c);
});
Rxjs Subject
をラップするメッセージサービスを使っています(TypeScript)
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs/Subject';
import { Subscription } from 'rxjs/Subscription';
import 'rxjs/add/operator/filter'
import 'rxjs/add/operator/map'
interface Message {
type: string;
payload: any;
}
type MessageCallback = (payload: any) => void;
@Injectable()
export class MessageService {
private handler = new Subject<Message>();
broadcast(type: string, payload: any) {
this.handler.next({ type, payload });
}
subscribe(type: string, callback: MessageCallback): Subscription {
return this.handler
.filter(message => message.type === type)
.map(message => message.payload)
.subscribe(callback);
}
}
コンポーネントはイベントを送信してブロードキャストすることができます(送信者):
import { Component, OnDestroy } from '@angular/core'
import { MessageService } from './message.service'
import { Subscription } from 'rxjs/Subscription'
@Component({
selector: 'sender',
template: ...
})
export class SenderComponent implements OnDestroy {
private subscription: Subscription;
private messages = [];
private messageNum = 0;
private name = 'sender'
constructor(private messageService: MessageService) {
this.subscription = messageService.subscribe(this.name, (payload) => {
this.messages.Push(payload);
});
}
send() {
let payload = {
text: `Message ${++this.messageNum}`,
respondEvent: this.name
}
this.messageService.broadcast('receiver', payload);
}
clear() {
this.messages = [];
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
(受信機)
import { Component, OnDestroy } from '@angular/core'
import { MessageService } from './message.service'
import { Subscription } from 'rxjs/Subscription'
@Component({
selector: 'receiver',
template: ...
})
export class ReceiverComponent implements OnDestroy {
private subscription: Subscription;
private messages = [];
constructor(private messageService: MessageService) {
this.subscription = messageService.subscribe('receiver', (payload) => {
this.messages.Push(payload);
});
}
send(message: {text: string, respondEvent: string}) {
this.messageService.broadcast(message.respondEvent, message.text);
}
clear() {
this.messages = [];
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
subscribe
のMessageService
メソッドはrxjs Subscription
オブジェクトを返します。
import { Subscription } from 'rxjs/Subscription';
...
export class SomeListener {
subscription: Subscription;
constructor(private messageService: MessageService) {
this.subscription = messageService.subscribe('someMessage', (payload) => {
console.log(payload);
this.subscription.unsubscribe();
});
}
}
また、この答えを参照してください: https://stackoverflow.com/a/36782616/1861779
サービス通信に EventEmitterを使用しないでください。
Observable型のいずれかを使うべきです。私は個人的にBehaviorSubjectが好きです。
簡単な例:
あなたは初期状態を渡すことができます、ここで私はnullを渡します
subject = new BehaviorSubject(null)とします。
件名を更新したいとき
subject.next(myObject)
任意のサービスまたはコンポーネントから観察し、それが新しいアップデートを取得したときに行動する.
subject.subscribe(this.YOURMETHOD);
これが詳細です。 。
あなたが使用することができます EventEmitterまたは あなたがDIに登録するイベントバスサービスを作成するために観測量。参加したいすべてのコンポーネントは、コンストラクタパラメータとしてサービスを要求し、イベントを発行および/または購読します。
また見なさい
私の好きなやり方は、私のサービスでビヘイビアサブジェクトまたはイベントエミッタ(ほぼ同じ)を使用して、すべてのサブコンポーネントを制御することです。
角度クリップを使用してng g sを実行して新しいサービスを作成してからBehaviorSubjectまたはEventEmitterを使用します。
export Class myService {
#all the stuff that must exist
myString: string[] = [];
contactChange : BehaviorSubject<string[]> = new BehaviorSubject(this.myString);
getContacts(newContacts) {
// get your data from a webservices & when you done simply next the value
this.contactChange.next(newContacts);
}
}
そうすることで、あなたのサービスをプロバイダとして使っているすべてのコンポーネントがその変更を認識するようになります。 eventEmitterと同じように結果を購読するだけです。
export Class myComp {
#all the stuff that exists like @Component + constructor using (private myService: myService)
this.myService.contactChange.subscribe((contacts) => {
this.contactList += contacts; //run everytime next is called
}
}
これは私のバージョンです:
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});
}
}
つかいます:
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 Subjectsを使ってObserverとObservablesをカスタムイベントの発行と購読のための一般的なソリューションとして結び付けることです。私のサンプルでは、デモ目的でcustomerオブジェクトを使用しています
this.pubSubService.Stream.emit(customer);
this.pubSubService.Stream.subscribe(customer => this.processCustomer(customer));
これもライブデモです: http://www.syntaxsuccess.com/angular-2-samples/#/demo/pub-sub
サービスイベント:コンポーネントはサービスイベントを購読できます。たとえば、2つの兄弟コンポーネントが同じサービスイベントを購読し、それぞれのモデルを変更することで応答できます。これについての詳細は下記をご覧ください。
ただし、親コンポーネントの破棄時には必ず登録を解除してください。
私達はあなたがあなた自身のコンポーネントでインスタンス化するイベントエミッターを通してすべてのモデル変更を送る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)
})
})
}
}
コンポーネントとサービス間で通信するもう1つの方法は、Ionicで使用されているイベントプロバイダを使用することです。
私は私の考えではもっと読みやすい "publish"と "subscribe"シンタクスを好む。
このサービスをAngularプロジェクトにコピーするだけです。
import {Injectable} from '@angular/core';
export type EventHandler = (...args: any[]) => any;
@Injectable({
providedIn: 'root',
})
export class Events {
private c = new Map<string, EventHandler[]>();
/**
* Subscribe to an event topic. Events that get posted to that topic will trigger the
provided handler.
*
* @param topic the topic to subscribe to
* @param handler the event handler
*/
subscribe(topic: string, ...handlers: EventHandler[]) {
let topics = this.c.get(topic);
if (!topics) {
this.c.set(topic, topics = []);
}
topics.Push(...handlers);
}
/**
* Unsubscribe from the given topic. Your handler will no longer receive events published to this topic.
*
* @param topic the topic to unsubscribe from
* @param handler the event handler
*
* @return true if a handler was removed
*/
unsubscribe(topic: string, handler?: EventHandler): boolean {
if (!handler) {
return this.c.delete(topic);
}
const topics = this.c.get(topic);
if (!topics) {
return false;
}
// We need to find and remove a specific handler
const index = topics.indexOf(handler);
if (index < 0) {
// Wasn't found, wasn't removed
return false;
}
topics.splice(index, 1);
if (topics.length === 0) {
this.c.delete(topic);
}
return true;
}
/**
* Publish an event to the given topic.
*
* @param topic the topic to publish to
* @param eventData the data to send as the event
*/
publish(topic: string, ...args: any[]): any[] | null {
const topics = this.c.get(topic);
if (!topics) {
return null;
}
return topics.map(handler => {
try {
return handler(...args);
} catch (e) {
console.error(e);
return null;
}
});
}
}
イオンイベントについてのより多くの情報: イオンイベントDoc