如何从父组件调用路由器出口子组件方法

Posted

技术标签:

【中文标题】如何从父组件调用路由器出口子组件方法【英文标题】:How to call router outlet child component method from parent comonent 【发布时间】:2018-02-07 12:32:15 【问题描述】:

我是 angular2 的新手

<parent>
    <router-outlet><router-outlet>
</parent>

我在父组件中有一个按钮,如果我单击该按钮,它应该调用子组件中的一个方法(在路由器插座中加载。)

有没有办法从父组件调用子组件(在路由器出口中)方法?

【问题讨论】:

在您的示例代码中,&lt;router outlet&gt; 应该是带有破折号的&lt;router-outlet&gt; 【参考方案1】:

显示组件的模板基本上有两种方式:作为嵌套组件或作为路由目标。

嵌套组件

如果您使用嵌套组件,则组件被认为具有“父组件/子组件”关系。

父组件的 html 将如下所示:

<div>
   <child></child>
</div>

其中“child”是子组件的选择器。然后,您可以使用 @Input 和 @Output 属性在两者之间进行通信。

如果您有嵌套组件,有关此技术的更多信息,请查看此处的文档:https://angular.io/guide/component-interaction

路由目标

如果通过&lt;router-outlet&gt;显示某个组件作为路由目标,则不存在“父组件”和“子组件”关系。

在这种情况下,组件之间通信的最佳方式是使用服务。

我在这里有一篇关于创建服务的博文:https://blogs.msmvps.com/deborahk/build-a-simple-angular-service-to-share-data/

资源

如果您是 Angular 新手,您可以通过教程或在线课程为自己节省大量时间和挫败感。这将介绍所有基本概念,让您快速上手 Angular。

您可以在此处完成“英雄之旅”教程:https://angular.io/tutorial

或者观看这样的课程:https://app.pluralsight.com/library/courses/angular-2-getting-started-update

或者这个:https://app.pluralsight.com/library/courses/angular-2-first-look/table-of-contents

(您可以免费注册一周。)

【讨论】:

【参考方案2】:

我找到了两种方法来实现这一点:

1.将主要组件注入子组件

您可以向主组件添加事件,将主组件注入子组件并订阅事件。请参阅说明这一点的plunk。但是,现在您的孩子依赖于您的主要组件。这可能不太好。

主要组件

executeAction: EventEmitter<any> = new EventEmitter();
constructor()  
performAction() 
  this.executeAction.emit();

孩子

constructor(private appComponent: AppComponent) 
    this.executeAction = this.executeAction.bind(this);
    eventSubscriber(appComponent.executeAction, this.executeAction);

ngOnDestroy(): void 
    eventSubscriber(this.appComponent.executeAction, this.executeAction, true);

executeAction() 
    alert('1');

2。实施服务

这里和Parent and children communicate via a service 中描述的最佳解决方案是创建一个服务,该服务将成为主要组件和子组件之间的附加层。这样,您将独立于主要组件实现。请参阅说明此方法的 plunk。

服务

subscription = new Subject();
executeAction() 
    this.subscription.next();

主要组件

constructor(private appService: AppService)  
performAction() 
  this.appService.executeAction();

孩子

constructor(private appService: AppService) 
    this.executeAction = this.executeAction.bind(this);
    eventSubscriber(appService.subscription, this.executeAction);

ngOnDestroy(): void 
    eventSubscriber(this.appService.subscription, this.executeAction, true);

executeAction() 
    alert('1');

【讨论】:

这很奏效...非常感谢。我的情况是在父组件中打开了一个对话框,该对话框直接更改了子组件(通过路由器插座),但服务对象没有正确反映在子组件中。不过,不确定这对资源来说有多昂贵? 这里的“eventSubscriber”是什么?我在相同的“找不到名称事件订阅者”上遇到错误【参考方案3】:

有了router-outlet 中的child,您可以使用ContentChild 来调用child 中的方法。所以...

import  ContentChild  from '@angular/core';

在你的父母中:

@ContentChild(ChildComponent)
private childComponent: ChildComponent;

然后在你的点击事件上做:

this.childComponent.doSomething()

您还需要将子组件添加到父组件的 providers 数组中:

@Component(
  selector: 'parent',
  ...
  providers: [ChildComponent]
)

【讨论】:

如果我错了,请纠正我,ContentChild 是预定义的,而 Childcomponent 是用户定义的。我试过了,但我无法从 undefined 调用 methodXXx Aaah,知道我忘记了一些东西:P 您需要在父级的 providers 数组中添加子级(无需在构造函数中注入它),编辑了我的答案 :) 现在应该可以工作了。而且您在评论中所做的假设是正确的。 最后一个问题,。它完美地调用了子方法。但是在子方法中。我重新分配一个变量。我没有看到基于新任务的 UI 渲染。请您提出建议。 嗯,这还不够信息。你能叉出我用来展示这个问题的 plunker 吗? 我已经创建了,plnkr.co/edit/e7HdIv9Mqeky00nHADvJ?p=preview 第一次单击加载子项,然后将子项加载到 routeroutlet 中。现在单击更改名称按钮。我可以从 App 组件调用该方法,但是在子组件中,名称元素未在路由器插座中呈现【参考方案4】:

我认为Event emitter 可以为您解决问题

虽然您也可以直接在子组件中使用它,但在这里使用公共服务将是一个好习惯

首先你需要在类似的服务中创建一个发射器

export class EmitterService 
   public nameEmitter:EventEmitter<string>=new EventEmitter(); 

    //method to get question
    changeName(name:string)   
        this.nameEmitter.emit(name)    
    


然后在根组件中注入服务依赖并调用更改名称方法来发出更改

 constructor(private emitter :EmitterService) 
 this.emitter.changeName("New Name");

最后在子组件中订阅更改

this.emitter.nameEmitter.subscribe(name=>this.name=name)

这里正在工作plunker

【讨论】:

【参考方案5】:

您可以使用shared service 与路由器添加的组件进行通信。

您还可以使用路由器出口的activate event 让父级知道路由器何时添加了组件

模板

<router-outlet (activate)="onActivate($event)"></router-outlet>

组件

  onActivate(componentRef)
    componentRef.works();
  

儿童补偿

 works()
    console.log("works");
  

【讨论】:

【参考方案6】:

从父组件调用router outlet子组件方法有两种方式。

    ViewChild/ViewChildren - 您可以使用 ViewChild/ViewChildren 从视图 DOM 中获取与选择器匹配的元素或指令。路由器出口渲染子组件无关紧要。

    从 '@angular/core' 导入 ViewChild ; @ViewChild(家庭组件) 私有子组件:HomeComponent;

然后你可以调用任何方法,

this.childComponent.changeTitle();

Demo - call router outlet child component method

    共享服务 - 由于 Angular 服务是单例的,通过将它们注入到您的控制器中,您可以调用方法。 (上面的答案已经提到了这种方法)

【讨论】:

【参考方案7】:

在我对这个问题进行了很多搜索之后,我找到了一个解决方案,并希望它可以帮助你。 contentChild/ViewChild 无法访问在 router-outlet 中加载的组件(及其数据),使用 sharedService 需要额外的文件和更多代码。

您只需要读取路由器插座中加载的组件:

    将模板引用添加到您的路由器插座:

    <router-outlet #myRouterOutlet ></router-outlet>
    

    从@angular/router 导入RouterOutlet:

    import ...
       RouterOutlet,
       .. from "@angular/router";
    

    最后,使用它:

    @ViewChild("myRouterOutlet",  static: true ) routerOutlet: RouterOutlet;
    buttonClicked()
        const childComp = this.routerOutlet.component as childComponent;
        childComp.childFunction();
    
    

希望对你有帮助:)

【讨论】:

【参考方案8】:

虽然是老问题了,但这里的回答并不完美。

通过使用服务或子实例,您将自己绑定到特定功能 您需要调用的名称。服务还强制你使用依赖注入。

有一个更好的方法,通过使用一个指令,使每一方完全独立 来自另一个。

指令是ng-router-outlet-comm。

【讨论】:

【参考方案9】:

这也可以通过以下方式实现:

.html

<parent>
    <router-outlet (activate)="onActivate($event)"><router-outlet>
</parent>

.ts

activatedComponentReference:any

onActivate(activatedComponentReference) 
  this.activatedComponentReference = activatedComponentReference;


onBtnClick() 
   const childRouteComp = this.activatedComponentReference as ChildRouteComponent;
   childRouteComp.childFunction();

【讨论】:

【参考方案10】:

上述解决方案在 Angular 10 中都不适合我。让我与您分享我为解决此问题而创建的 CustomEventHandler 服务:

@Injectable()
export class CustomEventHandler 

    private eventHandlersMap: Map<string, Function> = new Map();

    constructor() 
    

    public callEvent(eventName: string, data?: any): void 
        this.eventHandlersMap.get(eventName)(data);
    

    public addListener(eventName: string, callback: Function): void 
        this.eventHandlersMap.set(eventName, callback);
    

您可以在子组件构造函数中添加Listener,并从父组件中调用任何方法。

// add listener in child component
this.customEventHandler.addListener("some-event", this.someMethodToCall.bind(this));
// call this in parent component
this.customEventHandler.callEvent("some-event");

【讨论】:

以上是关于如何从父组件调用路由器出口子组件方法的主要内容,如果未能解决你的问题,请参考以下文章

Angular 2如何将变量从父组件传递到路由器出口

如何将回调从父组件传递到嵌套路由组件?

如何使用反应路由器将道具传递给非子组件?

如果尚未调用子组件,如何从父组件调用子组件方法?

如何从父组件调用子组件方法

如何从父组件调用子组件中的方法?