如何在angular2中调用另一个组件函数

我有两个组件如下,我想从另一个组件调用一个函数。这两个组件都包含在第三个父组件using指令中。

组件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,但我不清楚如何使用它以及如何调用该函数,有人可以帮助吗?

543996 次浏览

这取决于你的组件之间的关系(父/子),但最好的/通用的方式来实现通信组件是使用共享服务。

更多细节请参阅以下文档:

  • < a href = " https://angular.io/docs/ts/latest/cookbook/component-communication.html !#双向服务" rel="nofollow noreferrer">https://angular.io/docs/ts/latest/cookbook/component-communication.html#!# bidirectional-service < / >

也就是说,你可以使用下面的方法将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();
}
}

如果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()成为com2myEvent的事件处理程序:

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

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

  • 假设第一个组件是DbstatsMainComponent
  • 第二个组件DbstatsGraphComponent。
  • 第一个组件调用第二个方法
< p > <代码> & lt;按钮(点击)=“dbgraph.displayTableGraph()“祝辞Graph< / button> & lt; dbstats-graph # dbgraph> & lt; / dbstats-graph> < /代码> < / p >

注意子组件上的局部变量#dbgraph,父组件可以使用它来访问它的方法(dbgraph.displayTableGraph())。

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

你可以从组件2中访问组件1的方法。

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>

首先,您需要了解组件之间的关系。然后你可以选择正确的沟通方式。我将尝试解释我所知道的和在实践中用于组件之间通信的所有方法。

组件之间可以有什么样的关系?

1. 父母>孩子

enter image description here

通过输入共享数据

这可能是共享数据的最常用方法。它通过使用@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. 孩子>父母

enter image description here

通过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.兄弟姐妹

enter image description here

子>父>子

下面我试着解释兄弟姐妹之间的其他交流方式。但是你已经理解了上面方法的一种理解方式。

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. 不相关的组件

enter image description here

我在下面描述的所有方法都可以用于上述组件之间关系的所有选项。但每一种都有自己的优点和缺点。

与服务共享数据

当在缺少直接连接的组件之间传递数据时,例如兄弟姐妹、孙子姐妹等,您应该使用共享服务。当你的数据应该始终保持同步时,我发现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"];
});
}


}

< em > NgRx < / em >

最后一种更复杂但更强大的方法是使用NgRx。这个库不是用于数据共享的;它是一个功能强大的状态管理库。我不能用一个简短的例子来解释如何使用它,但你可以去官方网站阅读有关它的文档。

对我来说,NgRx Store解决了多个问题。例如,当你必须处理可观察数据时,当一些可观察数据的责任在不同的组件之间共享时,存储操作和减速器确保数据修改总是以“正确的方式”执行。

它还为HTTP请求缓存提供了可靠的解决方案。您将能够存储请求及其响应,以便您可以验证正在发出的请求还没有存储响应。

你可以阅读NgRx,了解你的应用程序是否需要它:

  • < a href = " https://blog.angular-university。>Angular的服务层:Redux, RxJs和Ngrx Store -什么时候使用一个Store,为什么?< / >
  • < a href = " https://blog.angular-university。Ngrx Store -一个架构指南 . io/angular- Ngrx Store -and-effects-crash-course/" rel="noreferrer">

最后,我想说的是,在选择一些共享数据的方法之前,您需要了解这些数据在未来将如何使用。我的意思是,也许现在你可以使用@Input装饰器来共享用户名和姓氏。然后您将添加一个新组件或新模块(例如,管理面板),它需要关于用户的更多信息。这意味着这可能是为用户数据使用服务或其他共享数据的更好方式。在开始实现数据共享之前,您需要进一步考虑这一点。

使用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")
}
在现实世界中,场景不是调用一个简单的函数,而是调用一个具有正确值的函数。让我们深入研究一下。这就是情境 用户需要从自己的组件中触发事件,并且在结束时还希望调用另一个组件的函数。假设两个组件

的服务文件是相同的

componentOne.html

    <button (click)="savePreviousWorkDetail()" data-dismiss="modal" class="btn submit-but" type="button">
Submit
</button>

当用户单击提交按钮时,他需要在自己的组件componentOne中调用savePreviousWorkDetail()。t,最后他还需要调用另一个组件的函数。为此,可以从componentOne调用服务类中的函数。ts,当它被调用时,componentTwo中的函数将被触发。

componentOne.ts

constructor(private httpservice: CommonServiceClass) {
}


savePreviousWorkDetail() {
// Things to be Executed in this function


this.httpservice.callMyMethod("Niroshan");
}

commontServiceClass.ts

import {Injectable,EventEmitter} from '@angular/core';


@Injectable()
export class CommonServiceClass{


invokeMyMethod = new EventEmitter();


constructor(private http: HttpClient) {
}


callMyMethod(params: any = 'Niroshan') {
this.invokeMyMethod.emit(params);
}


}

下面是componentTwo,它有需要从componentOne调用的函数。在ngOnInit()中,我们必须订阅被调用的方法,因此当它触发methodtobeccalled()时将被调用

componentTwo.ts

import {Observable,Subscription} from 'rxjs';




export class ComponentTwo implements OnInit {


constructor(private httpservice: CommonServiceClass) {
}


myMethodSubs: Subscription;


ngOnInit() {
    

this.myMethodSubs = this.httpservice.invokeMyMethod.subscribe(res => {
console.log(res);
this.methodToBeCalled();
});
    

methodToBeCalled(){
//what needs to done
}
}


}
  1. 向component2(或任何具有该方法的组件)添加可注入的装饰器
@Injectable({
providedIn: 'root'
})
  1. 注入到component1 (component2方法将被调用的组件)
constructor(public comp2 : component2) { }
  1. 在component1中定义调用component2方法的方法
method1()
{
this.comp2.method2();
}

组件1和组件2代码如下。

import {Component2} from './Component2';


@Component({
selector: 'sel-comp1',
templateUrl: './comp1.html',
styleUrls: ['./comp1.scss']
})
export class Component1 implements OnInit {
show = false;
constructor(public comp2: Component2) { }
method1()
{
this.comp2.method2();
}
}




@Component({
selector: 'sel-comp2',
templateUrl: './comp2.html',
styleUrls: ['./comp2.scss']
})
export class Component2 implements OnInit {
method2()
{
alert('called comp2 method from comp1');
}

我在父组件中使用触发器函数1(子函数),如下所示:

组件1(孩子):

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

组件2(父):

@Component(
selector:'com2',
template: `<button (click)="component1.function1()"
<com1 #component1></com1>`
)
export class Component2{
}

#component1是一个模板变量。您可以用任何名称替换它。(例:# hello1)

对于不相关的组件,使用共享服务使用这个简单的方法。

/ / YourService

private subject = new Subject<any>();
sendClickEvent() {
this.subject.next();
}
getClickEvent(): Observable<any>{
return this.subject.asObservable();
}
}

//有按钮的组件

clickMe(){
this.YourServiceObj.sendClickEvent();
}


<button (click)="clickMe()">Click Me</button>

//你接收点击事件的组件

    this.sharedService.getClickEvent().subscribe(()=>{
this.doWhateverYouWant();
}


)