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

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions