fix(project-editor): keep html-editor surface always in DOM

The @if/@else toggle between the contentEditable surface and the code
textarea broke the static ViewChild('surface') query: Angular never
resolves a static query for an element inside a conditional block, so
surface stayed undefined and every keystroke threw in emitChange().
Render both elements always and toggle visibility with [hidden]
instead, and add an ngAfterViewInit sync as a safety net for the
initial html input on the first change-detection pass.
This commit is contained in:
sdarbinyan
2026-07-13 09:31:04 +04:00
parent fdf11a7766
commit dc8c2eac91
2 changed files with 30 additions and 18 deletions

View File

@@ -6,19 +6,19 @@
<button type="button" (click)="toggleCode()">{{ showCode() ? 'Preview' : 'Code' }}</button> <button type="button" (click)="toggleCode()">{{ showCode() ? 'Preview' : 'Code' }}</button>
</div> </div>
@if (showCode()) {
<textarea <textarea
class="html-editor-code" class="html-editor-code"
rows="10" rows="10"
[hidden]="!showCode()"
[value]="codeValue()" [value]="codeValue()"
(input)="updateCode($any($event.target).value)" (input)="updateCode($any($event.target).value)"
></textarea> ></textarea>
} @else {
<div <div
#surface #surface
class="html-editor-surface" class="html-editor-surface"
[hidden]="showCode()"
contenteditable="true" contenteditable="true"
(input)="onInput()" (input)="onInput()"
></div> ></div>
}
</div> </div>

View File

@@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, Component, ElementRef, EventEmitter, Input, OnChanges, Output, SimpleChanges, ViewChild, signal } from '@angular/core'; import { AfterViewInit, ChangeDetectionStrategy, Component, ElementRef, EventEmitter, Input, OnChanges, Output, SimpleChanges, ViewChild, signal } from '@angular/core';
export interface HtmlEditorToolbarCommand { export interface HtmlEditorToolbarCommand {
id: string; id: string;
@@ -28,7 +28,7 @@ export const HTML_EDITOR_TOOLBAR: HtmlEditorToolbarCommand[] = [
styleUrls: ['./marketplace-html-editor.component.scss'], styleUrls: ['./marketplace-html-editor.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.OnPush
}) })
export class MarketplaceHtmlEditorComponent implements OnChanges { export class MarketplaceHtmlEditorComponent implements OnChanges, AfterViewInit {
@Input() html = ''; @Input() html = '';
@Output() htmlChange = new EventEmitter<string>(); @Output() htmlChange = new EventEmitter<string>();
@ViewChild('surface', { static: true }) surface!: ElementRef<HTMLDivElement>; @ViewChild('surface', { static: true }) surface!: ElementRef<HTMLDivElement>;
@@ -38,7 +38,19 @@ export class MarketplaceHtmlEditorComponent implements OnChanges {
readonly codeValue = signal(''); readonly codeValue = signal('');
ngOnChanges(changes: SimpleChanges): void { ngOnChanges(changes: SimpleChanges): void {
if (changes['html'] && this.surface && this.surface.nativeElement.innerHTML !== (this.html || '')) { if (changes['html']) {
this.syncSurfaceFromInput();
}
}
ngAfterViewInit(): void {
// Safety net: on the very first change detection pass, ngOnChanges can run
// before the static ViewChild is resolved, so re-sync once the view is ready.
this.syncSurfaceFromInput();
}
private syncSurfaceFromInput(): void {
if (this.surface && this.surface.nativeElement.innerHTML !== (this.html || '')) {
this.surface.nativeElement.innerHTML = this.html || ''; this.surface.nativeElement.innerHTML = this.html || '';
} }
} }