在ngx-summernote中插入Angular自定义元素:Input传值与Output事件绑定异常求助
问题描述
我一直使用ngx-summernote作为富文本编辑器,因项目需求需向其中动态插入自定义Angular组件。发现编辑器支持动态HTML插入但无法识别Angular组件,遂将组件转为Angular自定义元素,成功插入编辑器。但目前遇到两个问题:
- 向自定义元素的@Input()传递对象时,组件接收值为undefined;
- @Output()事件
okClicked无法触发绑定的testSpecOkClicked方法。
相关代码片段
ngx-summernote的HTML配置
<div [ngxSummernote]="summernoteOptions" [ngxSummernoteDisabled]="domain.screenIsViewOnly" [ngxSummernoteView]="specificInstructionRecord.specificInstruction" [(summernoteModel)]="specificInstructionRecord.specificInstruction" ></div>
自定义测试Angular组件
import { Component, EventEmitter, Input, OnInit, Output } from "@angular/core"; import lodash from 'lodash'; @Component({ selector: "spec-inst-test-component", templateUrl: "./specInstTestComponent.html" }) export class SpecInstTestComponent implements OnInit { @Input() input: any; @Output() okClicked = new EventEmitter<any>(); valueObject : { textVal: string, numberVal: number } textVal: string; numberVal: number; constructor() { } ngOnInit() { if(!lodash.isEmpty() && (typeof this.input) == "string") { this.input = JSON.parse(this.input); } } testButtonClicked() { this.valueObject = { textVal : this.textVal, numberVal: this.numberVal } this.okClicked.emit(this.valueObject); } }
自定义组件的模板HTML
<div> <div *ngIf="input.type == 'input'"> <label>{{input.label}}</label> <input type="text" [(ngModel)]="textVal"/> </div> <div *ngIf="input.type == 'number'"> <label>{{input.label}}</label> <input type="number" [(ngModel)]="numberVal"/> </div> <button (click)="testButtonClicked()">OK</button> </div>
将自定义组件转为自定义元素的过程
export class AppNgModule { constructor(private injector: Injector) { const specInstTestCustomElement = createCustomElement(SpecInstTestComponent, {injector: this.injector}); if(!customElements.get("spec-inst-test-element")) { customElements.define("spec-inst-test-element", specInstTestCustomElement); } } }
向summernote动态插入自定义元素的代码
addSpecInstTestEl() { this.inputObj = { type: "input", lable: "text field", } let html = `<spec-inst-test-element [input]="inputObj" (okClicked)="testSpecOkClicked($event)"></spec-inst-test-element>` this.specificInstructionRecord.specificInstruction = this.specificInstructionRecord.specificInstruction + html; }
问题原因及解决方案
这些问题并非ngx-summernote导致,而是自定义元素与Angular组件的绑定机制存在差异,且插入的HTML不在Angular的模板编译上下文范围内,需要按照原生自定义元素的规则处理:
1. 解决@Input()传递对象为undefined的问题
- 自定义元素不支持Angular的
[input]属性绑定语法,且对象类型的属性必须以JSON字符串形式传递(原生自定义元素只能接收字符串类型的属性值)。 - 修正
addSpecInstTestEl方法,将对象转为JSON字符串后通过原生属性传递:
addSpecInstTestEl() { this.inputObj = { type: "input", label: "text field", // 修正拼写错误:lable → label }; // 将对象转为JSON字符串,用原生属性传递 const html = `<spec-inst-test-element input='${JSON.stringify(this.inputObj)}'></spec-inst-test-element>`; this.specificInstructionRecord.specificInstruction += html; }
- 修正组件
ngOnInit中的lodash调用错误(原代码未传入参数):
ngOnInit() { // 修正:判断this.input是否为空,而非无参数调用 if(!lodash.isEmpty(this.input) && typeof this.input === "string") { this.input = JSON.parse(this.input); } }
2. 解决@Output()事件无法触发的问题
- Angular的
(okClicked)事件绑定语法仅在Angular模板中有效,插入到summernote的HTML属于原生DOM,无法被Angular解析。需将Angular的EventEmitter转为原生CustomEvent,并通过原生事件监听的方式绑定处理函数: - 修改组件的
testButtonClicked方法,用原生事件替代EventEmitter:
testButtonClicked() { this.valueObject = { textVal : this.textVal, numberVal: this.numberVal }; // 派发原生CustomEvent,将数据放在detail中 this.dispatchEvent(new CustomEvent('okClicked', { detail: this.valueObject })); }
- 在插入元素后,获取DOM元素并绑定原生事件监听:
addSpecInstTestEl() { this.inputObj = { type: "input", label: "text field", }; const html = `<spec-inst-test-element input='${JSON.stringify(this.inputObj)}'></spec-inst-test-element>`; this.specificInstructionRecord.specificInstruction += html; // 延迟获取元素(确保summernote已渲染插入的HTML) setTimeout(() => { const el = document.querySelector('spec-inst-test-element'); if (el) { el.addEventListener('okClicked', (event: CustomEvent) => { this.testSpecOkClicked(event.detail); }); } }, 0); }
内容的提问来源于stack exchange,提问作者Chinmoy Chakraborty
相关产品推荐
相关产品推荐

