Jak wywołać funkcje innych komponentów w angular2

154

Mam dwa składniki w następujący sposób i chcę wywołać funkcję z innego składnika. Oba komponenty są zawarte w trzecim nadrzędnym komponencie using.

Składnik 1:

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

Składnik 2:

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

Próbowałem użyć @inputi @outputale nie rozumiem dokładnie, jak go używać i jak wywołać tę funkcję, czy ktoś może pomóc?

noobProgrammer
źródło

Odpowiedzi:

130

Jeśli com1 i com2 są rodzeństwem, możesz użyć

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

com2 emituje zdarzenie przy użyciu pliku EventEmitter

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

Tutaj komponent nadrzędny dodaje powiązanie zdarzenia w celu nasłuchiwania myEventzdarzeń, a następnie wywołuje, com1.function1()gdy takie zdarzenie ma miejsce. #com1to zmienna szablonu, która pozwala odwołać się do tego elementu z innego miejsca w szablonie. Używamy tego dokonać function1()obsługi zdarzeń dla myEventod com2:

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

Aby zapoznać się z innymi opcjami komunikacji między komponentami, zobacz także interakcje komponentów

Günter Zöchbauer
źródło
Twoje pytanie nie dotyczy nic na temat rodzica-dziecka. Co próbujesz osiągnąć?
Günter Zöchbauer
kiedy zacząłeś mówić „Tutaj komponent nadrzędny dodaje powiązanie zdarzenia do nasłuchiwania myEvent”, jestem bardzo zdezorientowany. Myślałem, że próbujemy rozwiązać sytuację komponentu rodzeństwa. Łącznik kątowy to całe rodzicielskie dziecko, więc nie mogę ich używać.
Angela P
Jest rodzicem rodzeństwa (można też powiedzieć gospodarz). <sibling1 (myEvent)="...">jest powiązaniem zdarzenia dla rodzica / hosta, ponieważ jest to jedyny sposób, w jaki zapewnia Angular. Jednak program obsługi zdarzeń wywołuje metodę na drugiej siostrzanej ( com1). Rodzic pełni rolę mediatora.
Günter Zöchbauer
Jak to wywołać z widoku ?! Tylko w środku somecomponent.ts?
Mohammad Kermani
Ale jeśli dwa różne komponenty (zdarzenie On Click ((jeden komponent)) dla jakiegoś użytkownika na liście i skopiuj to zdarzenie kliknięcia na stronie szczegółów (inny komponent)) - Z metody One Component, której chcę użyć w innym komponencie, jak to zrobić Powiedz mi, Proszę ??? @ GünterZöchbauer
Jignesh Vagh
164

Po pierwsze, czego potrzebujesz, aby zrozumieć relacje między komponentami. Następnie możesz wybrać odpowiednią metodę komunikacji. Postaram się wyjaśnić wszystkie metody, które znam i wykorzystuję w swojej praktyce do komunikacji między komponentami.

Jakie mogą istnieć relacje między komponentami?

1. Rodzic> Dziecko

wprowadź opis obrazu tutaj

Udostępnianie danych poprzez wejście

Jest to prawdopodobnie najpopularniejsza metoda udostępniania danych. Działa przy użyciu@Input() dekoratora, aby umożliwić przekazywanie danych przez szablon.

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

}

To bardzo prosta metoda. Jest łatwy w użyciu. Możemy również wychwycić zmiany w danych w komponencie podrzędnym za pomocą ngOnChanges .

Ale nie zapominajmy, że jeśli użyjemy obiektu jako danych i zmienimy parametry tego obiektu, odniesienie do niego się nie zmieni. Dlatego jeśli chcemy otrzymać zmodyfikowany obiekt w komponencie potomnym, musi on być niezmienny.

2. Dziecko> Rodzic

wprowadź opis obrazu tutaj

Udostępnianie danych przez ViewChild

ViewChild umożliwia jednego komponentu do innego, dając rodzicowi dostęp do jego atrybutów i funkcji. Jest jednak jedno zastrzeżeniechild nie będą one dostępne do czasu zainicjowania widoku. Oznacza to, że musimy zaimplementować hak cyklu życia AfterViewInit, aby otrzymać dane od dziecka.

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

}

Udostępnianie danych przez Output () i EventEmitter

Innym sposobem udostępniania danych jest emitowanie danych od dziecka, które może wyświetlić rodzic. Takie podejście jest idealne, gdy chcesz udostępniać zmiany danych, które mają miejsce w przypadku kliknięć przycisków, wpisów formularzy i innych zdarzeń użytkownika.

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. Rodzeństwo

wprowadź opis obrazu tutaj

Dziecko> Rodzic> Dziecko

Poniżej staram się wyjaśnić inne sposoby komunikacji między rodzeństwem. Ale mogłeś już zrozumieć jeden ze sposobów zrozumienia powyższych metod.

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. Niepowiązane komponenty

wprowadź opis obrazu tutaj

Wszystkie metody, które opisałem poniżej, można zastosować do wszystkich powyższych opcji relacji między komponentami. Ale każdy ma swoje zalety i wady.

Udostępnianie danych usłudze

Podczas przekazywania danych między komponentami pozbawionymi bezpośredniego połączenia, takimi jak rodzeństwo, wnuki itp., Należy korzystać z usługi współdzielonej. Kiedy masz dane, które powinny być zawsze zsynchronizowane, uważam, że RxJS BehaviorSubject jest bardzo przydatne w tej sytuacji.

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

}

Udostępnianie danych trasom

Czasami trzeba nie tylko przekazywać proste dane między komponentami, ale także zapisać stan strony. Na przykład chcemy zapisać jakiś filtr na rynku online, a następnie skopiować ten link i wysłać znajomemu. Oczekujemy, że otworzy stronę w tym samym stanie co my. Pierwszym i prawdopodobnie najszybszym sposobem na zrobienie tego byłoby użycie parametrów zapytania .

Parametry zapytania wyglądają bardziej zgodnie z linią /people?id=gdzieid mogą być równe wszystkim i możesz mieć tyle parametrów, ile chcesz. Parametry zapytania byłyby oddzielone znakiem ampersand.

Podczas pracy z parametrami zapytania nie musisz definiować ich w pliku tras i można je nazwać parametrami. Na przykład weź następujący kod:

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

}

Na stronie odbiorczej otrzymasz następujące parametry zapytania, takie jak:

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

Ostatnim sposobem, który jest bardziej skomplikowany, ale potężniejszy, jest użycie NgRx . Ta biblioteka nie służy do udostępniania danych; jest to potężna biblioteka zarządzania stanem. Nie potrafię w krótkim przykładzie wyjaśnić, jak go używać, ale możesz wejść na oficjalną stronę i przeczytać dokumentację na jej temat.

Dla mnie NgRx Store rozwiązuje wiele problemów. Na przykład, gdy masz do czynienia z obserwowalnymi i gdy odpowiedzialność za pewne obserwowalne dane jest dzielona między różnymi komponentami, akcje przechowywania i reduktor zapewniają, że modyfikacje danych zawsze będą przeprowadzane „we właściwy sposób”.

Zapewnia również niezawodne rozwiązanie do buforowania żądań HTTP. Będziesz mógł przechowywać żądania i ich odpowiedzi, abyś mógł sprawdzić, czy żądanie, które składasz, nie ma jeszcze zapisanej odpowiedzi.

Możesz przeczytać o NgRx i zrozumieć, czy potrzebujesz go w swojej aplikacji, czy nie:

Na koniec chcę powiedzieć, że przed wybraniem niektórych metod udostępniania danych należy zrozumieć, w jaki sposób dane te będą wykorzystywane w przyszłości. Chodzi mi o to, że może teraz możesz użyć tylko @Inputdekoratora do udostępnienia nazwy użytkownika i nazwiska. Następnie dodasz nowy komponent lub nowy moduł (na przykład panel administracyjny), który potrzebuje więcej informacji o użytkowniku. Oznacza to, że może to być lepszy sposób korzystania z usługi dla danych użytkownika lub inny sposób udostępniania danych. Musisz się nad tym bardziej zastanowić, zanim zaczniesz wdrażać udostępnianie danych.

Roman Skydan
źródło
3
najlepsze wyjaśnienie z przykładami
afeef
1
najlepsza odpowiedź na ten temat, jaką kiedykolwiek widziałem, gratulacje…
Lonely
Doskonałe wyjaśnienie
Moisés Aguilar
w 3. Rodzeństwo nazwa wyświetlana ts child-two.component.ts w html powinna być childMessage (dla przypadku użyj pliku html)
Nam Nguyễn
84

Dostęp do metody składnika jeden można uzyskać z drugiego składnika.

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 plik html

<button (click)="callMe()">click</button>
Jayantha
źródło
2
To było dla mnie, dopóki nie musiałem na przykład uzyskać dostępu do zmiennej w componentOne z componentTwo, wywołując testCall .... przypadek, dla którego wartość zmiennej jest ustawiana przez componentOne, ale componentTwo otrzyma wartość domyślną, a nie bieżącą.
rey_coder
2
Nie otrzymuję wartości zmiennych componentOne wewnątrz metody testCall, jeśli wywołam testCall z componentTwo. Dowolny pomysł?
Raja
Proszę wyjaśnić tę metodę dla Ionic 4 z Angular 7
Mohammad Ayoub Khan
w ten sposób nie wyzwala się, że @Raja ma ten sam problem
Kevin Dias
1
THQQQQQQQQQQQ SO MUCH MAN
Ashu
33

Komponent 1 (dziecko):

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

Komponent 2 (rodzic):

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

  function2(){
    this.component1.function1();
  }
}
Ihor Khomiak
źródło
Dobra odpowiedź, zobacz także tutaj .
Szyfruj
w porządku, jak mam wywołać funkcję function2 w html? zawsze mam to. element 1 jest niezdefiniowany
cajuuh
<com1 # component1 (click) = "function2 ()"> </com1>
Ihor Khomiak,
1
To zadziałało dla mnie, kiedy zdałem sobie sprawę, że musisz zaimportować ViewChild z @ angular / core, a także Component1 z dowolnego miejsca.
Dallas Caley
1
Zamiast rozszerzać klasę komponentów, uesd, @ViewChildktóry działał dla mnie. Dzięki za ten przykład.
Yash
27

Zależy to od relacji między komponentami (rodzic / dziecko), ale najlepszym / ogólnym sposobem tworzenia komponentów komunikacyjnych jest użycie usługi współdzielonej.

Więcej informacji znajdziesz w tym dokumencie:

Mając to na uwadze, możesz użyć następujących elementów, aby zapewnić wystąpienie com1 w com2:

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

W com2 możesz użyć:

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

  function2(){
    // i want to call function 1 from com1 here
    this.com1ref.function1();
  }
}
Thierry Templier
źródło
Otrzymuję błąd Nie można powiązać z „com1Ref”, ponieważ nie jest to znana własność natywna
noobProgrammer
i nie bierze this.com1.function1 (); zamiast tego this.com1ref.function1 ();
noobProgrammer
Dzięki za wskazanie literówki! Czy masz @Inputna boisku?
Thierry Templier
ok to działa, czy możesz mi powiedzieć jak osiągnąć to samo w relacji rodzic - dziecko?
noobProgrammer
1
próbowałem tego samego dla rodzica dziecka, ale jest napisane, że funkcja1 () nie została przypisana
noobProgrammer
1
  • Powiedzmy, że pierwszym składnikiem jest DbstatsMainComponent
  • 2. składnik DbstatsGraphComponent.
  • 1. składnik wywołujący metodę 2nd

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

Zwróć uwagę na lokalną zmienną #dbgraphw komponencie potomnym, której rodzic może użyć, aby uzyskać dostęp do jego metod ( dbgraph.displayTableGraph()).

RAHUL KUMAR
źródło
0

Korzystając z Dataservice możemy wywołać funkcję z innego komponentu

Component1: Komponent, który nazywamy funkcją

constructor( public bookmarkRoot: dataService ) { } 

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

dataservice.ts

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

Komponent2: komponent zawierający funkcję

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

 closeDrawer() {
        console.log("this is called")
    }
Shafeeq Mohammed
źródło
może to wywołać wiele razy, aby uniknąć używania tego kodu w konstruktorzeif ( this.bookmarkRoot.callToggle.observers.length === 0 ) { this.bookmarkRoot.callToggle.subscribe(( data ) => { this.closeDrawer(); } ) }
Shafeeq Mohammed