Bug
CodeEditor.setValue() dispatches document changes without the External annotation, causing the component's _updateListener to treat programmatic writes as user edits.
Impact
When Angular Forms calls writeValue() during initialization (e.g., when a form control is bound with [(ngModel)]), the flow is:
writeValue() → setValue(value)
setValue() dispatches view.dispatch({ changes, annotations: [Transaction.addToHistory.of(false)] })
_updateListener sees docChanged && !tr.annotation(External) → calls _onChange()
- Angular marks the form control as dirty even though no user edit occurred
This causes forms containing <code-editor> to show false unsaved-changes state on load.
Root Cause
In code-editor.ts, setValue() includes Transaction.addToHistory.of(false) but does not include External.of(true):
setValue(value: string) {
this.view.dispatch({
changes: { from: 0, to: this.view.state.doc.length, insert: value },
annotations: Transaction.addToHistory.of(false),
});
}
The _updateListener already has the correct guard:
if (update.docChanged && !update.transactions.some(tr => tr.annotation(External))) {
// calls _onChange
}
The mechanism exists and works — setValue() simply doesn't use it.
Fix
Add External.of(true) to the annotations array in setValue():
setValue(value: string) {
this.view.dispatch({
changes: { from: 0, to: this.view.state.doc.length, insert: value },
annotations: [Transaction.addToHistory.of(false), External.of(true)],
});
}
Workaround
We are currently monkey-patching CodeEditor.prototype.setValue to add the annotation:
CodeEditor.prototype.setValue = function(value: string) {
if (!this.view) return;
this.view.dispatch({
changes: { from: 0, to: this.view.state.doc.length, insert: value },
annotations: [Transaction.addToHistory.of(false), External.of(true)],
});
};
Version
@acrodata/code-editor@0.6.0
Bug
CodeEditor.setValue()dispatches document changes without theExternalannotation, causing the component's_updateListenerto treat programmatic writes as user edits.Impact
When Angular Forms calls
writeValue()during initialization (e.g., when a form control is bound with[(ngModel)]), the flow is:writeValue()→setValue(value)setValue()dispatchesview.dispatch({ changes, annotations: [Transaction.addToHistory.of(false)] })_updateListenerseesdocChanged && !tr.annotation(External)→ calls_onChange()This causes forms containing
<code-editor>to show false unsaved-changes state on load.Root Cause
In
code-editor.ts,setValue()includesTransaction.addToHistory.of(false)but does not includeExternal.of(true):The
_updateListeneralready has the correct guard:The mechanism exists and works —
setValue()simply doesn't use it.Fix
Add
External.of(true)to the annotations array insetValue():Workaround
We are currently monkey-patching
CodeEditor.prototype.setValueto add the annotation:Version
@acrodata/code-editor@0.6.0