如何在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但是我不知道该如何使用以及如何调用该函数,任何人都可以帮忙吗?


Answers:


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()事件处理程序myEventcom2

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

有关组件之间通信的其他选项,另请参阅组件交互


您的问题不包含有关亲子的任何内容。您想完成什么?
君特Zöchbauer

当您开始说“父组件在此处添加事件绑定以侦听myEvent”时,我非常困惑。我以为我们正在尝试解决同级组件的情况。角度链接是所有父级孩子,所以我不能使用它们。
Angela P

它是rhe兄弟姐妹的父母(您也可以说是host)。<sibling1 (myEvent)="...">是父/主机的事件绑定,因为这是Angular提供的唯一方法。但是,事件处理程序会在另一个同级(com1)上调用方法。父母被用作调解人。
君特Zöchbauer

怎么称呼它呢?就在里面somecomponent.ts
Mohammad Kermani

但是如果两个不同的组件(对于某个用户在列表中单击事件((一个组件))并在详细信息页面上复制该单击事件(另一个组件))-我想在另一个组件中使用一个组件方法中的方法请告诉我 ???@GünterZöchbauer
Jignesh VAGH

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允许将一个组件注入另一个组件,从而使父级可以访问其属性和功能。然而,一个警告是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)
  }
}

子组件Two.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在这种情况下非常有用。

数据服务

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
带有示例的最佳解释
6

1
恭喜,这是我见过的对该主题的最佳答案,..
孤独的

外观极好的解释
莫伊塞斯阿吉拉尔

在3兄弟姐妹HTML中的TS儿童two.component.ts显示的名称应该是childMessage(为的情况下使用的HTML文件)
南阮

84

您可以从第二个组件访问一个组件的方法。

componentOne

  ngOnInit() {}

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

组件2

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中的变量的情况下,这才由我完成,在这种情况下,变量值由componentOne设置,但componentTwo将获得默认值而不是当前值。
rey_coder

2
如果我从componentTwo调用testCall,我不会在testCall方法中获取componentOne变量值。任何想法?
拉贾

请角7解释这个方法离子4
穆罕默德·阿尤布汗

通过这种方式,它不会触发@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

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();
noobProgrammer16年

感谢您指出错别字!你@Input在球场上吗?
Thierry Templier,2016年

好的,您能告诉我如何实现亲子关系吗?
noobProgrammer16年

1
我为亲子孩子尝试过相同的方法,但是它说undegeded的function1()
noobProgrammer16年

1
  • 可以说第一个组件是DbstatsMainComponent
  • 第二个组件DbstatsGraphComponent。
  • 第一个组件调用第二个方法

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

数据服务

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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.