angular - Angular 8 - 如何识别(和订阅)模板中传递给组件的元素
问题描述
我有一个渲染“项目”组件的父组件,使用模板来定义项目的标记(我已经将我的场景剥离到裸露的骨头):
// parent component template
<item [model]="itemModel" [template]="itemTemplate"></item>
<ng-template #itemTemplate let-model="model">
<div>AAA {{model.property1}}</div>
<div>BBB {{model.property2}}</div>
<div>CCC {{model.property3}}</div>
</ng-template>
// "item" component template
<ng-container [ngTemplateOutlet]="template" [ngTemplateOutletContext]="{model: model}"></ng-container>
在“item”组件代码中,我需要订阅#itemTemplate 中的一个元素(例如“AAA”div)的点击事件。问题是它是父组件定义了#itemTemplate 中的哪些元素将被订阅。这可以由开发人员在父组件中硬编码(如何准确地做到这一点是这里问题的一部分)。同时,点击处理程序需要在“item”组件内部(封装点击处理功能——父组件不需要关心)。
我尝试了三种方法来做到这一点,但我都不喜欢,我想知道是否有更好的方法。这 3 次尝试是相互独立的。
1.使用模板引用变量
这个想法:就像#itemTemplate 引用用于标识模板一样,使用相同的语法来创建对所需组件的引用(要订阅)。然后通过新的@Input 将引用传递给“item”组件。
// parent component template
// the "item" component now takes an additional input "subscribeToThis"
<item [model]="itemModel" [template]="itemTemplate" [subscribeToThis]="subscribeToThis"></item>
<ng-template #itemTemplate let-model="model">
// the #subscribeToThis reference on the div below is used above as the value
// for the subscribeToThis input of the child component;
// but it's always undefined ...
<div #subscribeToThis >AAA {{model.property1}}</div>
<div>BBB {{model.property2}}</div>
<div>CCC {{model.property3}}</div>
</ng-template>
结果/问题:在“item”组件中,subscribeToThis 是未定义的。(我认为它是未定义的,因为 #subscribeToThis 引用的范围 - 我是对的吗?)因此“item”组件不能使用它来查找要订阅的元素。
与此类似,结果相同 - 我尝试在“item”组件类中找到#subscribeToThis 引用,如下所示:
// bits from child component class
@ViewChild('subscribeToThis', {static: false}) elementToSubscribeTo: TemplateRef<any>;
ngAfterViewInit() {
// this.elementToSubscribeTo is undefined here;
// if "things worked", this.elementToSubscribeTo would refer to the element to whose click event I need to subscibe to
}
但 elementToSubscribeTo 再次未定义。
2.使用css类标记和查找元素
想法:为所需的#itemTemplate 元素分配一个特殊的类(在下面的示例中为“subscribe-to-this”):
// parent component template
<ng-template #itemTemplate let-model="model">
// note the class on this div; you can use low-level tools to find the element
// by its class, but that's not "clean angular"
<div class="subscribe-to-this">AAA {{model.property1}}</div>
<div>BBB {{model.property2}}</div>
<div>CCC {{model.property3}}</div>
</ng-template>
然后,在“item”组件中,将 ElementRef 和 Renderer2 注入其中。使用它们,我可以按类查找元素,然后订阅其单击事件。
结果/成功: “item”组件将找到元素,并且订阅元素的 click 事件会触发“item”组件中的点击处理程序。
这种方法的问题:有充分的理由不鼓励这种低级方法。
3.直接在模板中的元素中分配点击处理程序
想法:
// parent component template
<ng-template #itemTemplate let-model="model">
// the click handler is assigned right here in the template;
// note that the event handler is a function on the model
// that is passed to the child component; that function needs to be
// added to the model "artificially" since the model shouldn't need to be aware
// of how it's handled in the "item" component; that's one of the bad points
// in this attempt
<div (click)="model.clickHandler()">AAA {{model.property1}}</div>
<div>BBB {{model.property2}}</div>
<div>CCC {{model.property3}}</div>
</ng-template>
为此,必须将 clickHandler() 函数添加到模型中(例如,在“item”组件的 onNgInit 中)。这种体操的原因是#itemTemplate 中的用法相对简单。
结果/成功:单击正确的元素时调用 clickHandler()。
这种方法的问题:
父组件必须知道魔术函数名称。这不是一个交易破坏者,但距离一个漂亮干净的黑盒组件只有一步之遥。
如果消费者(父母)之后需要处理 itemModel 数据,那么“附加”函数 clickHandler() 就不太好了。当父级返回添加了一些“垃圾”的数据时,这并不好。我知道我可以从对象中删除它,但这感觉就像是兔子洞的开始。
结论
我是 Angular 的新手,我希望我只是缺少一些技术,可以让我干净地实现这一点,同时保持“项目”组件尽可能“黑盒子”。感谢您的任何想法或见解。
解决方案
您只需要在上下文中提供 click 方法,例如
// "item" component template
<ng-container [ngTemplateOutlet]="template" [ngTemplateOutletContext]="{model: model, onClickItem: doStuffWhenClick}"></ng-container>
// "item" component ts file
public doStuffWhenClick(...) { ... }
// parent component template
<item [model]="itemModel" [template]="itemTemplate"></item>
<ng-template #itemTemplate let-model="model" let-onClickItem="onClickItem">
<div (click)="onClickItem()">AAA {{model.property1}}</div>
<div>BBB {{model.property2}}</div>
<div>CCC {{model.property3}}</div>
</ng-template>
推荐阅读
- visual-studio - 为什么没有在 Visual Studio 2019 中添加项目引用?
- php - 更新不适用于外键上的 INNER JOIN
- java - 我们如何在从应用程序传递电子邮件意图时动态设置来自电子邮件
- casting - 将 u32 重新解释为四个 u8 的数组
- java - 加载时编织(AspectJ):Hystrix 中断事务传播
- python - 在项目中执行“npm install”时出现“gyp ERR!stack SyntaxError: invalid syntax:”
- ios - 获取请求中的令牌标头不起作用。生成错误 403 响应
- php - 查找字符串中第一个重复的单词
- .net-core - 为源重试“FindPackagesByIdAsync”
- intellij-idea - 错误:无法启动 javac.exe。exec:“javac.exe”:在 intellij 上运行仪表命令后,在 %PATH 中找不到可执行文件