Как вызвать функцию другого компонента в angular2

156

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

Компонент 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но я не понимаю , как именно использовать его и как вызывать эту функцию, кто может помочь?

noobProgrammer
источник

Ответы:

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

Для других вариантов взаимодействия между компонентами см. Также взаимодействие компонентов

Гюнтер Цохбауэр
источник
Ваш вопрос не содержит ничего о родительском ребенке. Что вы пытаетесь достичь?
Гюнтер Цохбауэр
Когда вы начали говорить «Здесь родительский компонент добавляет привязку события для прослушивания myEvent», я очень запутался. Я думал, что мы пытаемся решить ситуацию с компонентом родного брата. Угловая ссылка - это все родительские дочерние элементы, поэтому я не могу их использовать.
Анжела П,
Это родитель братьев и сестер (вы также можете сказать, хозяин). <sibling1 (myEvent)="...">это привязка события для родителя / хоста, потому что это единственный способ, который обеспечивает Angular. Однако обработчик событий вызывает метод для другого sibling ( com1). Родитель используется в качестве посредника.
Гюнтер Цохбауэр
Как это назвать вне поля зрения ?! Просто внутри somecomponent.ts?
Мохаммед Кермани
Но если два разных компонента (событие «По щелчку» ((один компонент)) для какого-то пользователя в списке и скопировать это событие щелчка на странице сведений (другой компонент)) - Из метода «Один компонент» я хочу использовать в другом компоненте, как это сделать? Пожалуйста, скажите мне ??? @ GünterZöchbauer
Джинеш Ваг
165

Во-первых, что вам нужно, чтобы понять отношения между компонентами. Тогда вы можете выбрать правильный способ общения. Я попытаюсь объяснить все методы, которые я знаю и использую в своей практике для взаимодействия между компонентами.

Какие могут быть отношения между компонентами?

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

дети-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)
  }
}

дети-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 Store решает несколько проблем. Например, когда вам приходится иметь дело с наблюдаемыми и когда ответственность за некоторые наблюдаемые данные распределяется между различными компонентами, действия хранилища и редуктор гарантируют, что изменения данных всегда будут выполняться «правильным образом».

Он также обеспечивает надежное решение для кэширования HTTP-запросов. Вы сможете хранить запросы и их ответы, чтобы убедиться, что на ваш запрос еще не сохранен ответ.

Вы можете прочитать о NgRx и понять, нужно ли вам это в вашем приложении или нет:

Наконец, я хочу сказать, что перед выбором некоторых методов обмена данными вам необходимо понять, как эти данные будут использоваться в будущем. Я имею в виду, может быть, только сейчас вы можете использовать только @Inputдекоратор для обмена именем пользователя и фамилией. Затем вы добавите новый компонент или новый модуль (например, панель администратора), который нуждается в дополнительной информации о пользователе. Это означает, что это может быть лучшим способом использования службы для пользовательских данных или каким-либо другим способом обмена данными. Вы должны подумать об этом больше, прежде чем начать осуществлять обмен данными.

Роман Скайдан
источник
3
лучшее объяснение с примерами
afeef
1
лучший ответ на эту тему, который я когда-либо видел, поздравляю ..
Одинокий
Отличное объяснение
Мойз Агилар
в 3. Братья и сестры, отображаемое имя ts child-two.component.ts в html должно быть childMessage (для случая используйте html файл)
Nam Nguyễn
85

Вы можете получить доступ к методу компонента один из компонента два ..

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>
Jayantha
источник
2
Это было для меня, пока мне не понадобилось, например, получить доступ к переменной в componentOne из componentTwo, вызвав testCall .... случай, для которого значение переменной устанавливается componentOne, но componentTwo получит значение по умолчанию, а не текущий.
rey_coder
2
Я не получаю значения переменных componentOne внутри метода testCall, если я вызываю testCall из componentTwo. Любая идея?
Раджа
Пожалуйста, объясните этот метод для Ionic 4 с Angular 7
Мохаммад Аюб Хан
с этим способом это не вызывает @Raja, имеющий ту же самую проблему
Кевин Диас
1
THQQQQQQQQQQQ ТАК МНОГО ЧЕЛОВЕКА
Ашу
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 # component1 (click) = "function2 ()"> </ com1>
Игорь Хомяк,
1
Это сработало для меня, как только я понял, что вам нужно импортировать ViewChild из @ angular / core, а также Component1, где бы он ни находился.
Даллас Кейли
1
Вместо того, чтобы расширять класс компонента, я использовал, @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 (); вместо этого this.com1ref.function1 ();
noobProgrammer
Спасибо за указание на опечатку! У вас есть @Inputна поле?
Тьерри Темплиер
Хорошо, это работает, можете ли вы сказать мне, как добиться того же для родителя - ребенка отношения?
noobProgrammer
1
я попробовал то же самое для родительского ребенка, но он говорит, что function1 () из undegined
noobProgrammer
1
  • Допустим, 1-й компонент - это DbstatsMainComponent
  • 2-й компонент DbstatsGraphComponent.
  • 1-й компонент, вызывающий метод 2-го

<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