Skip to content

setValue() should include External annotation to prevent false onChange during writeValue() #14

@mmurphy-resolve

Description

@mmurphy-resolve

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:

  1. writeValue()setValue(value)
  2. setValue() dispatches view.dispatch({ changes, annotations: [Transaction.addToHistory.of(false)] })
  3. _updateListener sees docChanged && !tr.annotation(External) → calls _onChange()
  4. 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions