angular2で別のコンポーネント関数を呼び出す方法


154

次の2つのコンポーネントがあり、別のコンポーネントから関数を呼び出したいです。両方のコンポーネントは、ディレクティブを使用して3番目の親コンポーネントに含まれています。

コンポーネント1:

@component(
    selector:'com1'
)
export class com1{
    function1(){...}
}

コンポーネント2:

@component(
    selector:'com2'
)
export class com2{
    function2(){...
        // i want to call function 1 from com1 here
    }
}

私が使って試してみた@input@output私はそれを使用する方法を正確に理解していないとどのようにその機能を呼び出すために、缶誰の助け?


回答:


130

com1とcom2が兄弟である場合、使用できます

@component({
  selector:'com1',
})
export class com1{
  function1(){...}
}

com2は、 EventEmitter

@component({
  selector:'com2',
  template: `<button (click)="function2()">click</button>`
)
export class com2{
  @Output() myEvent = new EventEmitter();
  function2(){...
    this.myEvent.emit(null)
  }
}

ここで、親コンポーネントはイベントバインディングを追加してイベントをリッスンし、そのようなイベントが発生したときにmyEvent呼び出しますcom1.function1()#com1テンプレートの他の場所からこの要素を参照できるようにするテンプレート変数です。これを使用function1()してmyEventof のイベントハンドラーを作成しますcom2

@component({
  selector:'parent',
  template: `<com1 #com1></com1><com2 (myEvent)="com1.function1()"></com2>`
)
export class com2{
}

コンポーネント間で通信する他のオプションについては、component-interactionも参照してください


あなたの質問には、親子については何も含まれていません。何を成し遂げようとしますか?
ギュンターZöchbauer

「ここで親コンポーネントがmyEventをリッスンするためのイベントバインディングを追加する」と言ったとき、私は非常に混乱しています。私たちは兄弟コンポーネントの状況を解決しようとしていると思いました。角度リンクはすべて親子なので、使用できません。
アンジェラP

これは兄弟の親です(ホストと言うこともできます)。<sibling1 (myEvent)="...">これは、Angularが提供する唯一の方法であるため、親/ホストのイベントバインディングです。ただし、イベントハンドラーは他の兄弟(com1)のメソッドを呼び出します。親はメディエーターとして使用されます。
ギュンターZöchbauer

それをビューの外に呼び出すには?!ちょうど中にsomecomponent.ts
Mohammad Kermani 2017

しかし、2つの異なるコンポーネント(On Clickイベント((1つのコンポーネント))がリスト内のユーザーにあり、そのクリックイベントを詳細ページにコピーしている場合(別のコンポーネント))-1つのコンポーネントメソッドから別のコンポーネントで使用したい場合、その方法教えてください ???@GünterZöchbauer– Jignesh Vagh 2017
12:03

164

まず、コンポーネント間の関係を理解するために必要なこと。次に、適切な通信方法を選択できます。私が知っている、コンポーネント間の通信のための私の実践で使用するすべての方法を説明しようと思います。

コンポーネント間にはどのような関係がありますか?

1.親>子

ここに画像の説明を入力してください

入力を介したデータの共有

これはおそらくデータを共有する最も一般的な方法です。これは、@Input()デコレーターを使用して機能し、テンプレートを介してデータを渡すことができます。

parent.component.ts

import { Component } from '@angular/core';

@Component({
  selector: 'parent-component',
  template: `
    <child-component [childProperty]="parentProperty"></child-component>
  `,
  styleUrls: ['./parent.component.css']
})
export class ParentComponent{
  parentProperty = "I come from parent"
  constructor() { }
}

child.component.ts

import { Component, Input } from '@angular/core';

@Component({
  selector: 'child-component',
  template: `
      Hi {{ childProperty }}
  `,
  styleUrls: ['./child.component.css']
})
export class ChildComponent {

  @Input() childProperty: string;

  constructor() { }

}

これは非常に単純な方法です。使い方は簡単です。ngOnChangesを使用して、子コンポーネントのデータへの変更をキャッチすることもできます。

ただし、オブジェクトをデータとして使用し、このオブジェクトのパラメーターを変更しても、そのオブジェクトへの参照は変更されないことを忘れないでください。したがって、子コンポーネントで変更されたオブジェクトを受け取りたい場合、それは不変でなければなりません。

2.子>親

ここに画像の説明を入力してください

ViewChildによるデータの共有

ViewChildを使用すると、1つのコンポーネントを別のコンポーネントに注入して、親がその属性と機能にアクセスできるようにすることができます。ただし、1つの注意点はchild、ビューが初期化されるまで利用できないことです。つまり、子からデータを受け取るには、AfterViewInitライフサイクルフックを実装する必要があります。

parent.component.ts

import { Component, ViewChild, AfterViewInit } from '@angular/core';
import { ChildComponent } from "../child/child.component";

@Component({
  selector: 'parent-component',
  template: `
    Message: {{ message }}
    <child-compnent></child-compnent>
  `,
  styleUrls: ['./parent.component.css']
})
export class ParentComponent implements AfterViewInit {

  @ViewChild(ChildComponent) child;

  constructor() { }

  message:string;

  ngAfterViewInit() {
    this.message = this.child.message
  }
}

child.component.ts

import { Component} from '@angular/core';

@Component({
  selector: 'child-component',
  template: `
  `,
  styleUrls: ['./child.component.css']
})
export class ChildComponent {

  message = 'Hello!';

  constructor() { }

}

Output()およびEventEmitterを介したデータの共有

データを共有するもう1つの方法は、親からリストできる子からデータを送信することです。このアプローチは、ボタンのクリック、フォームのエントリ、その他のユーザーイベントなどで発生するデータの変更を共有する場合に最適です。

parent.component.ts

import { Component } from '@angular/core';

@Component({
  selector: 'parent-component',
  template: `
    Message: {{message}}
    <child-component (messageEvent)="receiveMessage($event)"></child-component>
  `,
  styleUrls: ['./parent.component.css']
})
export class ParentComponent {

  constructor() { }

  message:string;

  receiveMessage($event) {
    this.message = $event
  }
}

child.component.ts

import { Component, Output, EventEmitter } from '@angular/core';

@Component({
  selector: 'child-component',
  template: `
      <button (click)="sendMessage()">Send Message</button>
  `,
  styleUrls: ['./child.component.css']
})
export class ChildComponent {

  message: string = "Hello!"

  @Output() messageEvent = new EventEmitter<string>();

  constructor() { }

  sendMessage() {
    this.messageEvent.emit(this.message)
  }
}

3.兄弟

ここに画像の説明を入力してください

子>親>子

以下の兄弟間で通信する他の方法を説明しようとします。しかし、上記の方法を理解する方法の1つをすでに理解している可能性があります。

parent.component.ts

import { Component } from '@angular/core';

@Component({
  selector: 'parent-component',
  template: `
    Message: {{message}}
    <child-one-component (messageEvent)="receiveMessage($event)"></child1-component>
    <child-two-component [childMessage]="message"></child2-component>
  `,
  styleUrls: ['./parent.component.css']
})
export class ParentComponent {

  constructor() { }

  message: string;

  receiveMessage($event) {
    this.message = $event
  }
}

child-one.component.ts

import { Component, Output, EventEmitter } from '@angular/core';

@Component({
  selector: 'child-one-component',
  template: `
      <button (click)="sendMessage()">Send Message</button>
  `,
  styleUrls: ['./child-one.component.css']
})
export class ChildOneComponent {

  message: string = "Hello!"

  @Output() messageEvent = new EventEmitter<string>();

  constructor() { }

  sendMessage() {
    this.messageEvent.emit(this.message)
  }
}

child-two.component.ts

import { Component, Input } from '@angular/core';

@Component({
  selector: 'child-two-component',
  template: `
       {{ message }}
  `,
  styleUrls: ['./child-two.component.css']
})
export class ChildTwoComponent {

  @Input() childMessage: string;

  constructor() { }

}

4.無関係なコンポーネント

ここに画像の説明を入力してください

以下で説明するすべてのメソッドは、コンポーネント間の関係に関する上記のすべてのオプションに使用できます。しかし、それぞれに独自の利点と欠点があります。

サービスとのデータの共有

兄弟、孫など、直接接続されていないコンポーネント間でデータを渡す場合は、共有サービスを使用する必要があります。常に同期する必要があるデータがある場合、RxJS BehaviorSubjectはこの状況で非常に役立ちます。

data.service.ts

import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';

@Injectable()
export class DataService {

  private messageSource = new BehaviorSubject('default message');
  currentMessage = this.messageSource.asObservable();

  constructor() { }

  changeMessage(message: string) {
    this.messageSource.next(message)
  }

}

first.component.ts

import { Component, OnInit } from '@angular/core';
import { DataService } from "../data.service";

@Component({
  selector: 'first-componennt',
  template: `
    {{message}}
  `,
  styleUrls: ['./first.component.css']
})
export class FirstComponent implements OnInit {

  message:string;

  constructor(private data: DataService) {
      // The approach in Angular 6 is to declare in constructor
      this.data.currentMessage.subscribe(message => this.message = message);
  }

  ngOnInit() {
    this.data.currentMessage.subscribe(message => this.message = message)
  }

}

second.component.ts

import { Component, OnInit } from '@angular/core';
import { DataService } from "../data.service";

@Component({
  selector: 'second-component',
  template: `
    {{message}}
    <button (click)="newMessage()">New Message</button>
  `,
  styleUrls: ['./second.component.css']
})
export class SecondComponent implements OnInit {

  message:string;

  constructor(private data: DataService) { }

  ngOnInit() {
    this.data.currentMessage.subscribe(message => this.message = message)
  }

  newMessage() {
    this.data.changeMessage("Hello from Second Component")
  }

}

ルートとデータを共有する

コンポーネント間で単純なデータを渡すだけでなく、ページの一部の状態を保存する必要がある場合があります。たとえば、フィルターをオンライン市場に保存してから、このリンクをコピーして友達に送信したいとします。そして、私たちと同じ状態でページを開くことが期待されます。これを行う最初の、おそらくおそらく最も速い方法は、クエリパラメータを使用することです。

クエリパラメータは/people?id=、where idが何にでも等しくなるように、より多くのパラメータを表示でき、必要な数のパラメータを持つことができます。クエリパラメータは、アンパサンド文字で区切られます。

クエリパラメータを使用する場合、ルートファイルで定義する必要はなく、名前付きパラメータにすることができます。たとえば、次のコードを見てください。

page1.component.ts

import {Component} from "@angular/core";
import {Router, NavigationExtras} from "@angular/router";

@Component({
    selector: "page1",
  template: `
    <button (click)="onTap()">Navigate to page2</button>
  `,
})
export class Page1Component {

    public constructor(private router: Router) { }

    public onTap() {
        let navigationExtras: NavigationExtras = {
            queryParams: {
                "firstname": "Nic",
                "lastname": "Raboy"
            }
        };
        this.router.navigate(["page2"], navigationExtras);
    }

}

受信ページでは、これらのクエリパラメータを次のように受信します。

page2.component.ts

import {Component} from "@angular/core";
import {ActivatedRoute} from "@angular/router";

@Component({
    selector: "page2",
    template: `
         <span>{{firstname}}</span>
         <span>{{lastname}}</span>
      `,
})
export class Page2Component {

    firstname: string;
    lastname: string;

    public constructor(private route: ActivatedRoute) {
        this.route.queryParams.subscribe(params => {
            this.firstname = params["firstname"];
            this.lastname = params["lastname"];
        });
    }

}

NgRx

より複雑ですがより強力な最後の方法は、NgRxを使用することです。このライブラリはデータ共有用ではありません。強力な状態管理ライブラリです。簡単な例ではそれを使用する方法を説明することはできませんが、公式サイトにアクセスして、それに関するドキュメントを読むことができます。

私にとって、NgRxストアは複数の問題を解決します。たとえば、オブザーバブルを処理する必要があり、いくつかのオブザーバブルデータの責任が異なるコンポーネント間で共有される場合、ストアアクションとリデューサーは、データの変更が常に「正しい方法」で実行されるようにします。

また、HTTPリクエストのキャッシュのための信頼できるソリューションを提供します。リクエストとそのレスポンスを保存できるので、作成しているリクエストにレスポンスが保存されていないことを確認できます。

NgRxについて読んで、アプリでそれが必要かどうかを理解できます。

最後に、データを共有する方法をいくつか選択する前に、このデータが将来どのように使用されるかを理解する必要があると言いたいです。@Inputつまり、たった今、ユーザー名と姓を共有するためにデコレーターを使用できるようになったということです。次に、ユーザーに関する詳細情報を必要とする新しいコンポーネントまたは新しいモジュール(管理パネルなど)を追加します。これは、ユーザーデータのサービスを使用したり、データを共有したりするためのより良い方法である可能性があることを意味します。データ共有の実装を開始する前に、それについてもっと考える必要があります。


3
例と最良の説明
afeef

1
私が今まで見たことを、このトピックへの最良の答え、おめでとう..
ロンリー

優秀な説明
モイセスアギラール

3兄弟にHTMLでTS-子two.component.tsの表示名は、(ケース用htmlファイル用)childMessageする必要があります
ナム・グエン

84

コンポーネント2からコンポーネント1のメソッドにアクセスできます。

componentOne

  ngOnInit() {}

  public testCall(){
    alert("I am here..");    
  }

componentTwo

import { oneComponent } from '../one.component';


@Component({
  providers:[oneComponent ],
  selector: 'app-two',
  templateUrl: ...
}


constructor(private comp: oneComponent ) { }

public callMe(): void {
    this.comp.testCall();
  }

componentTwo htmlファイル

<button (click)="callMe()">click</button>

2
たとえば、testCallを呼び出してcomponentTwoからcomponentOneの変数にアクセスする必要があるまでは、これでした。
rey_coder

2
componentTwoからtestCallを呼び出した場合、testCallメソッドの内部にcomponentOne変数値が取得されません。何か案が?
ラジャ

Ionic 4とAngular 7のこの方法について説明してください
Mohammad Ayoub Khan

この方法では、@ Rajaが同じ問題を引き起こすことはありません
Kevin Dias

1
THQQQQQQQQQQQ SO MUCH MAN
アッシュ

33

コンポーネント1(子):

@Component(
  selector:'com1'
)
export class Component1{
  function1(){...}
}

コンポーネント2(親):

@Component(
  selector:'com2',
  template: `<com1 #component1></com1>`
)
export class Component2{
  @ViewChild("component1") component1: Component1;

  function2(){
    this.component1.function1();
  }
}


どうやってhtmlでfunction2を呼び出すのですか?私はいつもthis.component1が定義されていました
cajuuh

<com1#component1(click)= "function2()"> </ com1>
Ihor Khomiak

1
@ angular / coreからViewChildをインポートする必要があり、どこからでもComponent1をインポートする必要があることに気付いたら、これは私にとってはうまくいきました。
ダラスケーリー

1
コンポーネントクラスを拡張する代わりに、私@ViewChildはうまくいきました。この例をありがとう。
Yash 2018

27

コンポーネント(親/子)間の関係に依存しますが、通信コンポーネントを作成するための最良/一般的な方法は、共有サービスを使用することです。

詳細については、このドキュメントを参照してください。

つまり、次のコマンドを使用して、com1のインスタンスをcom2に提供できます。

<div>
  <com1 #com1>...</com1>
  <com2 [com1ref]="com1">...</com2>
</div>

com2では、以下を使用できます。

@Component({
  selector:'com2'
})
export class com2{
  @Input()
  com1ref:com1;

  function2(){
    // i want to call function 1 from com1 here
    this.com1ref.function1();
  }
}

既知のネイティブプロパティではないため、エラー 'com1Ref'にバインドできません
noobProgrammer

そしてthis.com1.function1();を取りません。代わりにthis.com1ref.function1();
noobProgrammer

入力ミスを指摘していただきありがとうございます。あなたは@Inputフィールドにいますか?
ティエリーテンプリエ

了解しました。親子関係で同じようにする方法を教えていただけますか?
noobProgrammer

1
私は親の子供にも同じことを試しましたが、未定義のfunction1()と表示されます
noobProgrammer

1
  • 最初のコンポーネントがDbstatsMainComponentであるとしましょう
  • 2番目のコンポーネントDbstatsGraphComponent。
  • 2番目のメソッドを呼び出す1番目のコンポーネント

<button (click)="dbgraph.displayTableGraph()">Graph</button> <dbstats-graph #dbgraph></dbstats-graph>

#dbgraph親がそのメソッドにアクセスするために使用できる子コンポーネントのローカル変数に注意してください(dbgraph.displayTableGraph())。


0

Dataserviceを使用して、別のコンポーネントから関数を呼び出すことができます

Component1:関数を呼び出すコンポーネント

constructor( public bookmarkRoot: dataService ) { } 

onClick(){
     this.bookmarkRoot.callToggle.next( true );
}

dataservice.ts

import { Injectable } from '@angular/core';
@Injectable()
export class dataService {
     callToggle = new Subject();
}

Component2:関数を含むコンポーネント

constructor( public bookmarkRoot: dataService ) { 
  this.bookmarkRoot.callToggle.subscribe(( data ) => {
            this.closeDrawer();
        } )
} 

 closeDrawer() {
        console.log("this is called")
    }

これは、コンストラクター内でこのコードを使用することを回避するために複数回呼び出す可能性がありますif ( this.bookmarkRoot.callToggle.observers.length === 0 ) { this.bookmarkRoot.callToggle.subscribe(( data ) => { this.closeDrawer(); } ) }
Shafeeq Mohammed
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.