So rufen Sie eine andere Komponentenfunktion in angle2 auf

154

Ich habe zwei Komponenten wie folgt und möchte eine Funktion von einer anderen Komponente aufrufen. Beide Komponenten werden mithilfe der Direktive in die dritte übergeordnete Komponente aufgenommen.

Komponente 1:

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

Komponente 2:

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

Ich habe es versucht @inputund @outputaber ich verstehe nicht genau, wie man es benutzt und wie man diese Funktion aufruft. Kann mir jemand helfen?

noobProgrammer
quelle

Antworten:

130

Wenn com1 und com2 Geschwister sind, können Sie verwenden

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

com2 gibt ein Ereignis mit einem aus EventEmitter

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

Hier fügt die übergeordnete Komponente eine Ereignisbindung hinzu, um myEventEreignisse abzuhören, und ruft dann auf, com1.function1()wenn ein solches Ereignis eintritt. #com1ist eine Vorlagenvariable, mit der auf dieses Element von einer anderen Stelle in der Vorlage verwiesen werden kann. Wir nutzen dies , um function1()für die Event - Handler myEventvon com2:

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

Weitere Optionen für die Kommunikation zwischen Komponenten finden Sie unter Komponenteninteraktion

Günter Zöchbauer
quelle
Ihre Frage enthält nichts über Eltern-Kind. Was versuchst du zu erreichen?
Günter Zöchbauer
Als Sie anfingen zu sagen "Hier fügt die übergeordnete Komponente eine Ereignisbindung hinzu, um myEvent abzuhören", bin ich sehr verwirrt. Ich dachte, wir versuchen, die Situation der Geschwisterkomponenten zu lösen. Der Winkellink ist alles Eltern-Kind, daher kann ich diese nicht verwenden.
Angela P
Es ist das Elternteil der Geschwister (man kann auch Gastgeber sagen). <sibling1 (myEvent)="...">ist eine Ereignisbindung für das übergeordnete Element / den Host, da Angular nur so eine Möglichkeit bietet. Der Ereignishandler ruft jedoch eine Methode für das andere sibling ( com1) auf. Der Elternteil wird als Vermittler eingesetzt.
Günter Zöchbauer
Wie nenne ich es aus der Sicht?! Nur in der somecomponent.ts?
Mohammad Kermani
Aber wenn zwei verschiedene Komponenten (On-Click-Ereignis ((eine Komponente)) für einen Benutzer in der Liste und kopieren Sie dieses Klickereignis auf der Detailseite (eine andere Komponente)) - Von einer Komponentenmethode, die ich in einer anderen Komponente verwenden möchte, wie geht das? Erzähl es mir bitte ??? @ GünterZöchbauer
Jignesh Vagh
164

Zunächst, was Sie benötigen, um die Beziehungen zwischen Komponenten zu verstehen. Dann können Sie die richtige Kommunikationsmethode wählen. Ich werde versuchen, alle Methoden zu erklären, die ich kenne und in meiner Praxis für die Kommunikation zwischen Komponenten verwende.

Welche Arten von Beziehungen zwischen Komponenten kann es geben?

1. Eltern> Kind

Geben Sie hier die Bildbeschreibung ein

Daten über Eingabe teilen

Dies ist wahrscheinlich die häufigste Methode zum Teilen von Daten. Mithilfe des @Input()Dekorators können Daten über die Vorlage übertragen werden.

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() { }

}

Dies ist eine sehr einfache Methode. Es ist einfach zu bedienen. Mit ngOnChanges können wir auch Änderungen an den Daten in der untergeordneten Komponente abfangen .

Vergessen Sie jedoch nicht, dass sich der Verweis darauf nicht ändert, wenn wir ein Objekt als Daten verwenden und die Parameter dieses Objekts ändern. Wenn wir also ein geändertes Objekt in einer untergeordneten Komponente empfangen möchten, muss es unveränderlich sein.

2. Kind> Eltern

Geben Sie hier die Bildbeschreibung ein

Daten über ViewChild teilen

Mit ViewChild kann eine Komponente in eine andere eingefügt werden, sodass der übergeordnete Benutzer Zugriff auf seine Attribute und Funktionen erhält . Eine Einschränkung ist jedoch, dass childdiese erst verfügbar ist, nachdem die Ansicht initialisiert wurde. Dies bedeutet, dass wir den AfterViewInit-Lebenszyklus-Hook implementieren müssen, um die Daten vom untergeordneten Element zu empfangen.

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() { }

}

Daten über Output () und EventEmitter austauschen

Eine andere Möglichkeit, Daten gemeinsam zu nutzen, besteht darin, Daten vom untergeordneten Element auszugeben, die vom übergeordneten Element aufgelistet werden können. Dieser Ansatz ist ideal, wenn Sie Datenänderungen freigeben möchten, die beispielsweise bei Schaltflächenklicks, Formulareinträgen und anderen Benutzerereignissen auftreten.

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. Geschwister

Geben Sie hier die Bildbeschreibung ein

Kind> Eltern> Kind

Ich versuche unten andere Möglichkeiten der Kommunikation zwischen Geschwistern zu erklären. Sie können jedoch bereits eine der Möglichkeiten zum Verständnis der oben genannten Methoden verstehen.

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. Nicht verwandte Komponenten

Geben Sie hier die Bildbeschreibung ein

Alle Methoden, die ich unten beschrieben habe, können für alle oben genannten Optionen für die Beziehung zwischen den Komponenten verwendet werden. Aber jeder hat seine eigenen Vor- und Nachteile.

Daten mit einem Dienst teilen

Wenn Sie Daten zwischen Komponenten übertragen, für die keine direkte Verbindung besteht, z. B. Geschwister, Enkelkinder usw., sollten Sie einen gemeinsam genutzten Dienst verwenden. Wenn Sie Daten haben, die immer synchron sein sollten, finde ich das RxJS BehaviorSubject in dieser Situation sehr nützlich.

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")
  }

}

Daten mit einer Route teilen

Manchmal müssen Sie nicht nur einfache Daten zwischen Komponenten übertragen, sondern auch einen bestimmten Status der Seite speichern. Zum Beispiel möchten wir einige Filter im Online-Markt speichern und dann diesen Link kopieren und an einen Freund senden. Und wir erwarten, dass die Seite im selben Zustand wie wir geöffnet wird. Der erste und wahrscheinlich schnellste Weg, dies zu tun, wäre die Verwendung von Abfrageparametern .

Abfrageparameter sehen eher so aus, /people?id=als idob alles gleich wäre, und Sie können so viele Parameter haben, wie Sie möchten. Die Abfrageparameter würden durch das kaufmännische Und-Zeichen getrennt.

Wenn Sie mit Abfrageparametern arbeiten, müssen Sie diese nicht in Ihrer Routendatei definieren, und sie können als Parameter bezeichnet werden. Nehmen Sie zum Beispiel den folgenden Code:

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);
    }

}

Auf der Empfangsseite würden Sie diese Abfrageparameter wie folgt erhalten:

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

Der letzte Weg, der komplizierter, aber leistungsfähiger ist, ist die Verwendung von NgRx . Diese Bibliothek ist nicht für die gemeinsame Nutzung von Daten vorgesehen. Es ist eine leistungsstarke State-Management-Bibliothek. Ich kann nicht in einem kurzen Beispiel erklären, wie man es benutzt, aber Sie können auf die offizielle Seite gehen und die Dokumentation darüber lesen.

Für mich löst NgRx Store mehrere Probleme. Wenn Sie sich beispielsweise mit Observablen befassen müssen und die Verantwortung für einige beobachtbare Daten auf verschiedene Komponenten aufgeteilt ist, stellen die Speicheraktionen und der Reduzierer sicher, dass Datenänderungen immer "auf die richtige Weise" durchgeführt werden.

Es bietet auch eine zuverlässige Lösung für das Zwischenspeichern von HTTP-Anforderungen. Sie können die Anforderungen und ihre Antworten speichern, um zu überprüfen, ob für die von Ihnen gestellte Anforderung noch keine Antwort gespeichert ist.

Sie können über NgRx lesen und verstehen, ob Sie es in Ihrer App benötigen oder nicht:

Abschließend möchte ich sagen, dass Sie vor der Auswahl einiger Methoden zum Teilen von Daten verstehen müssen, wie diese Daten in Zukunft verwendet werden. Ich meine, vielleicht können Sie gerade jetzt nur einen @InputDekorateur verwenden, um einen Benutzernamen und einen Nachnamen zu teilen. Anschließend fügen Sie eine neue Komponente oder ein neues Modul hinzu (z. B. ein Admin-Bereich), das weitere Informationen zum Benutzer benötigt. Dies bedeutet, dass dies möglicherweise eine bessere Möglichkeit ist, einen Dienst für Benutzerdaten zu verwenden oder Daten auf andere Weise gemeinsam zu nutzen. Sie müssen mehr darüber nachdenken, bevor Sie mit der Implementierung der Datenfreigabe beginnen.

Roman Skydan
quelle
3
beste Erklärung mit Beispielen
Afeef
1
Die beste Antwort auf dieses Thema, die ich je gesehen habe, herzlichen Glückwunsch, ..
Lonely
Hervorragende Erklärung
Moisés Aguilar
in 3. Geschwister sollte der Anzeigename ts child-two.component.ts in HTML childMessage sein (für den Fall HTML-Datei verwenden)
Nam Nguyễn
84

Sie können von Komponente 2 auf die Methode der Komponente 1 zugreifen.

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-Datei

<button (click)="callMe()">click</button>
Jayantha
quelle
2
Dies war es für mich, bis ich zum Beispiel von componentTwo aus auf eine Variable in componentOne zugreifen musste, indem ich testCall aufrief .... der Fall, für den der Variablenwert von componentOne festgelegt wird, componentTwo jedoch den Standardwert und nicht den aktuellen Wert erhält.
rey_coder
2
Ich erhalte keine variablen Werte für componentOne in der testCall-Methode, wenn ich testCall von componentTwo aus aufrufe. Irgendeine Idee?
Raja
Bitte erklären Sie diese Methode für Ionic 4 mit Angular 7
Mohammad Ayoub Khan
auf diese Weise hat es nicht Triger @ Raja das gleiche Problem
Kevin Dias
1
THQQQQQQQQQQQ SO VIEL MANN
Ashu
33

Komponente 1 (Kind):

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

Komponente 2 (übergeordnet):

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

  function2(){
    this.component1.function1();
  }
}
Ihor Khomiak
quelle
Gute Antwort, siehe auch hier .
Chiffre
Alles klar, wie ich function2 auf HTML aufrufe? Ich habe immer this.component1 ist undefiniert
cajuuh
<com1 # component1 (click) = "function2 ()"> </ com1>
Ihor Khomiak
1
Dies funktionierte für mich, als mir klar wurde, dass Sie ViewChild von @ angle / core und auch Component1 von überall importieren mussten.
Dallas Caley
1
Anstatt die Komponentenklasse zu erweitern, habe ich @ViewChilddas für mich funktioniert. Danke für dieses Beispiel.
Yash
27

Dies hängt von der Beziehung zwischen Ihren Komponenten (Eltern / Kind) ab. Der beste / allgemeinste Weg, um Kommunikationskomponenten herzustellen, ist die Verwendung eines gemeinsam genutzten Dienstes.

Weitere Informationen finden Sie in diesem Dokument:

Davon abgesehen können Sie Folgendes verwenden, um eine Instanz von com1 in com2 bereitzustellen:

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

In com2 können Sie Folgendes verwenden:

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

  function2(){
    // i want to call function 1 from com1 here
    this.com1ref.function1();
  }
}
Thierry Templier
quelle
Ich erhalte eine Fehlermeldung Kann nicht an 'com1Ref' gebunden werden, da es sich nicht um eine bekannte native Eigenschaft handelt
noobProgrammer
und es nimmt nicht this.com1.function1 (); stattdessen this.com1ref.function1 ();
NoobProgrammer
Vielen Dank für den Hinweis auf den Tippfehler! Hast du das @Inputauf dem Feld?
Thierry Templier
ok es funktioniert, kannst du mir sagen, wie ich dasselbe für die Eltern-Kind-Beziehung erreichen kann?
NoobProgrammer
1
Ich habe das gleiche für Eltern Kind versucht, aber es sagt Funktion1 () von undegined
noobProgrammer
1
  • Nehmen wir an, die erste Komponente ist DbstatsMainComponent
  • 2. Komponente DbstatsGraphComponent.
  • 1. Komponente, die die Methode des 2. aufruft

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

Beachten Sie die lokale Variable #dbgraphin der untergeordneten Komponente, mit der das übergeordnete Element auf seine Methoden zugreifen kann ( dbgraph.displayTableGraph()).

RAHUL KUMAR
quelle
0

Mit Dataservice können wir die Funktion von einer anderen Komponente aus aufrufen

Komponente1: Die Komponente, die wir die Funktion aufrufen

constructor( public bookmarkRoot: dataService ) { } 

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

datenservice.ts

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

Komponente2: Die Komponente, die die Funktion enthält

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

 closeDrawer() {
        console.log("this is called")
    }
Shafeeq Mohammed
quelle
Dies kann mehrmals aufgerufen werden, um zu vermeiden, dass dieser Code im Konstruktor verwendet wirdif ( this.bookmarkRoot.callToggle.observers.length === 0 ) { this.bookmarkRoot.callToggle.subscribe(( data ) => { this.closeDrawer(); } ) }
Shafeeq Mohammed