Як викликати функцію інших компонентів у angular2


154

У мене є два компоненти наступним чином, і я хочу викликати функцію з іншого компонента. Обидва компоненти включені в третій батьківський компонент, використовуючи директиву.

Компонент 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()обробник подій для myEventз com2:

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

Інші варіанти спілкування між компонентами див. Також взаємодія компонентів


Ваше запитання не містить нічого про батька-дитини. Що ви намагаєтеся досягти?
Günter Zöchbauer

коли ви почали говорити "Тут батьківський компонент додає події, обов'язкові для прослуховування myEvent", я дуже розгублений. Я думав, що ми намагаємося вирішити ситуацію із складною частиною. Кутова ланка є всіма батьками, тому я не можу їх використовувати.
Angela P

Це батько рідних братів і сестер (можна також сказати господаря). <sibling1 (myEvent)="...">є подіями, обов'язковими для батьків / хостів, оскільки це єдиний спосіб, який надає Angular. Однак обробник подій викликає метод іншого братів ( com1). Батько використовується як посередник.
Гюнтер Зехбауер

Як викликати це з виду ?! Просто всередині somecomponent.ts?
Мохаммед Кермані

Але якщо два різних компонента (подія при натисканні ((один компонент)) для якогось користувача у списку та скопіюйте цю подію на сторінці деталей (інший компонент) - з методу одного компонента, який я хочу використовувати в іншому компоненті, як це зробити Будь-ласка скажи мені ??? @ GünterZöchbauer
Jignesh Vagh

164

По-перше, що потрібно для розуміння зв’язків між компонентами. Тоді ви можете вибрати правильний спосіб спілкування. Я спробую пояснити всі методи, які я знаю і використовую у своїй практиці для спілкування між компонентами.

Які існують відносини між компонентами?

1. Батько> Дитина

введіть тут опис зображення

Обмін даними через Input

Це, мабуть, найпоширеніший метод обміну даними. Це працює за допомогою @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 дозволяє вводити один компонент в інший, надаючи батькові доступ до його атрибутів та функцій. Однак одне застереження - це те, що воно 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

Ще один спосіб обміну даними - це передавання даних від дитини, які можуть бути перелічені батьком. Цей підхід ідеально підходить, коли ви хочете поділитися змінами даних, які відбуваються в таких речах, як натискання кнопки, записи форми та інші події користувача.

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. Брати і сестри

введіть тут опис зображення

Дитина> Батько> Дитина

Я намагаюся пояснити інші способи спілкування між побратимами нижче. Але ви вже могли зрозуміти один із способів розуміння вищевказаних методів.

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=де 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
найкраща відповідь на цю тему, яку я коли-небудь бачив, вітаю, ..
Самотній

Відмінне пояснення
Moisés Aguilar

в 3. Брати і сестри, відображене ім'я ts child-two.component.ts у html має бути дочірньою Повідомлення (для випадку використання html-файлу)
Нам Нгуїн

84

Ви можете отримати доступ до методу компонента з другого компонента.

компонентОдин

  ngOnInit() {}

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

компонентДві

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


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


constructor(private comp: oneComponent ) { }

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

компонент Два файли html

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

2
Це було для мене, поки мені не потрібно було, наприклад, отримати доступ до змінної в компонентOne з компонентаTwo, викликаючи testCall .... випадок, для якого значення змінної встановлюється компонентомOne, але компонентTwo отримає за замовчуванням, а не поточне.
rey_coder

2
Я не отримую значень змінних компонентівOne всередині методу testCall, якщо я викликаю testCall від компонента компонентаTwo. Будь-яка ідея?
Раджа

Поясніть, будь ласка, цей метод для Ionic 4 з кутовим 7
Мохаммед Аюб Хан

таким чином він не спрацьовує @Raja має таку ж проблему
Кевін Діас

1
THQQQQQQQQQQ Так багато людей
Ашу

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

Гарна відповідь, дивіться також тут .
Шифр

все в порядку, як я дзвоню function2 в HTML? Я завжди отримував this.component1 не визначено
cajuuh

<com1 # компонент1 (натисніть) = "function2 ()"> </com1>
Ігор Хомяк

1
Це працювало для мене, як тільки я зрозумів, що вам доведеться імпортувати ViewChild з @ angular / core, а також Component1 з будь-якого місця.
Даллас Кейлі

1
Замість розширення класу компонентів, я uesd, @ViewChildякий працював на мене. Дякую за цей приклад.
Яш

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 (); замість цього.com1ref.function1 ();
noobProgrammer

Дякуємо, що вказали на друкарську помилку! У вас є @Inputна полі?
Тьеррі Темплієр

ок його робота, чи можете ви сказати мені, як досягти того ж стосунку батьків - дитини ??
noobProgrammer

1
Я спробував те ж саме для батьків-батьків, але він говорить, що функція 1 () невизначена
noobProgrammer

1
  • Скажімо, перший компонент - це DbstatsMainComponent
  • 2-й компонент DbstatsGraphComponent.
  • 1-й компонент, що викликає метод 2-го

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

Зверніть увагу на локальну змінну #dbgraphдочірнього компонента, яку батько може використовувати для доступу до своїх методів ( dbgraph.displayTableGraph()).


0

За допомогою Dataservice ми можемо викликати функцію з іншого компонента

Компонент1: компонент, який ми викликаємо функцією

constructor( public bookmarkRoot: dataService ) { } 

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

dataservice.ts

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

Компонент2: компонент, який містить функцію

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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.