Angular子组件值传递问题:无法获取WYSIWYG编辑器(Summernote)的HTML内容值
嘿,我看了你的代码,问题的核心是:你的app-editor自定义组件没有实现Angular表单的ControlValueAccessor接口,这导致父组件的FormControl无法和它进行双向的数据同步,所以你才会一直拿不到编辑器的HTML内容。
要解决这个问题,我们需要让子组件适配Angular的表单系统,具体步骤如下:
1. 实现ControlValueAccessor接口并注册提供者
首先,在SummernoteComponent中导入相关的表单接口,并实现ControlValueAccessor,同时在组件装饰器中注册NG_VALUE_ACCESSOR提供者,这样Angular才会把你的组件当成一个可复用的表单控件。
修改summernote.component.ts的顶部导入:
import { Component, Input, OnInit, forwardRef } from "@angular/core"; import { FormControl, FormGroup, Validators, ControlValueAccessor, NG_VALUE_ACCESSOR } from "@angular/forms"; import { DomSanitizer } from "@angular/platform-browser";
然后更新组件装饰器,添加提供者:
@Component({ selector: 'app-editor', templateUrl: './summernote.component.html', styleUrls: ['./summernote.component.css'], providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => SummernoteComponent), multi: true } ] })
接下来让组件类实现ControlValueAccessor接口,并添加必要的方法和属性:
export class SummernoteComponent implements OnInit, ControlValueAccessor { showTemplateForm = true; // 移除原来的@Input() html,因为我们通过ControlValueAccessor处理值 html?: string; form: FormGroup; config: any = { /* 你的配置保持不变 */ }; editorDisabled = false; // ControlValueAccessor相关属性和方法 private onChange: (value: string) => void = () => {}; private onTouched: () => void = () => {}; get sanitizedHtml() { return this.sanitizer.bypassSecurityTrustHtml(this.html || ''); } constructor(private sanitizer: DomSanitizer) { this.form = new FormGroup({ html: new FormControl('', Validators.required), }); // 监听表单控件的变化,通知父组件 this.form.get('html')?.valueChanges.subscribe(value => { this.html = value; this.onChange(value); this.onTouched(); }); } ngOnInit() {} // ControlValueAccessor方法:写入值到组件 writeValue(value: string): void { this.html = value || ''; this.form.get('html')?.setValue(this.html); } // ControlValueAccessor方法:注册值变化回调 registerOnChange(fn: (value: string) => void): void { this.onChange = fn; } // ControlValueAccessor方法:注册触摸回调 registerOnTouched(fn: () => void): void { this.onTouched = fn; } // ControlValueAccessor方法:设置组件禁用状态 setDisabledState(isDisabled: boolean): void { this.editorDisabled = isDisabled; if (isDisabled) { this.form.get('html')?.disable(); } else { this.form.get('html')?.enable(); } } // 其他原有方法保持不变 onBlur() { console.log('Blur'); this.onTouched(); } onDelete(file: { url: any; }) { console.log('Delete file', file.url); } summernoteInit(event: any) { console.log(event); } }
2. 调整子组件模板的绑定
修改summernote.component.html中的textarea绑定,确保它和组件的form控件关联,同时移除原来的[(ngModel)](因为我们用Reactive Forms结合ControlValueAccessor):
<ng-container *ngIf="showTemplateForm"> <form [formGroup]="form"> <textarea name="html" formControlName="html" [ngxSummernote]="config" [ngxSummernoteDisabled]="editorDisabled" (blur)="onBlur()" (mediaDelete)="onDelete($event)" (summernoteInit)="summernoteInit($event)"> </textarea> </form> <!-- 其他预览部分保持不变 --> <h3>Vorschau:</h3> <div [ngxSummernoteView]="html"></div> <h3>Code</h3> <pre>{{html}}</pre> <div *ngIf="!html || html === ''"> Required </div> </ng-container>
3. 父组件的简化调整
在app.component.ts中,确保你导入了FormBuilder(看起来你之前漏了导入):
import { Component, OnInit } from '@angular/core'; import { FormBuilder, FormControl, FormGroup } from '@angular/forms'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements OnInit { htmlTest = ""; form!: FormGroup; constructor(private fb: FormBuilder) {} ngOnInit(): void { this.createForm(); } createForm() { this.form = this.fb.group({ myEditor: new FormControl(this.htmlTest), }) } get myEditor(): FormControl{ return this.form.controls['myEditor'] as FormControl; } // 添加提交测试方法 onSubmit() { console.log('编辑器内容:', this.myEditor.value); } }
父组件模板app.component.html可以保持不变,但你可以点击提交按钮测试是否能获取值:
<form class="input-form" [formGroup]="form" (submit)="onSubmit()"> <app-editor formControlName="myEditor"></app-editor> <button mat-raised-button color="primary" type="submit">Speichern</button> </form>
为什么这样修改?
Angular的Reactive Forms需要自定义组件实现ControlValueAccessor接口来完成以下工作:
writeValue: 接收父组件传递过来的值,更新子组件内部状态registerOnChange: 注册一个回调函数,当子组件值变化时通知父组件registerOnTouched: 注册一个回调函数,标记控件为已触摸(比如失去焦点时)setDisabledState: 响应父组件设置的禁用状态
通过这些方法,你的自定义编辑器组件就能和父组件的FormControl完全同步数据了,现在你点击提交按钮就能看到控制台输出编辑器的HTML内容了。
内容的提问来源于stack exchange,提问作者Svensen

