如何从父组件调用路由器出口子组件方法
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>
我在父组件中有一个按钮,如果我单击该按钮,它应该调用子组件中的一个方法(在路由器插座中加载。)
有没有办法从父组件调用子组件(在路由器出口中)方法?
【问题讨论】:
在您的示例代码中,<router outlet>
应该是带有破折号的<router-outlet>
。
【参考方案1】:
显示组件的模板基本上有两种方式:作为嵌套组件或作为路由目标。
嵌套组件
如果您使用嵌套组件,则组件被认为具有“父组件/子组件”关系。
父组件的 html 将如下所示:
<div>
<child></child>
</div>
其中“child”是子组件的选择器。然后,您可以使用 @Input 和 @Output 属性在两者之间进行通信。
如果您有嵌套组件,有关此技术的更多信息,请查看此处的文档:https://angular.io/guide/component-interaction
路由目标
如果通过<router-outlet>
显示某个组件作为路由目标,则不存在“父组件”和“子组件”关系。
在这种情况下,组件之间通信的最佳方式是使用服务。
我在这里有一篇关于创建服务的博文: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");
【讨论】:
以上是关于如何从父组件调用路由器出口子组件方法的主要内容,如果未能解决你的问题,请参考以下文章