Skip to content

Programmatic control

Every PhoneInput mutator runs the same pipeline as a keystroke: the controller resolves the next state, the adapter writes the value and caret to the element, and subscribers receive the new PhoneInputState. This page covers the methods that drive the input from code and the lifecycle rules around them.

The examples assume an international input with a fixed plus:

import { ensureEngineReady } from '@telixon/core';
import { createPhoneInput } from '@telixon/web-sdk';
await ensureEngineReady();
const phoneInput = createPhoneInput({
mode: 'international',
input: inputElement,
display: { callingCodeInInput: true, plusPrefix: 'fixed' },
});

setValue(value) replaces the current value as a single parse-and-format operation: the string is read as if it were typed into an empty input in one step, and the result is reformatted under the active mode and display configuration. Digits and a leading plus carry meaning; formatting characters in the string do not, so a raw digit string and a preformatted one produce the same state.

phoneInput.setValue('+380501234567');
phoneInput.getState().value; // '+380 50 123 4567'
phoneInput.getState().region; // 'UA'

The caret lands at the end of the new value.

In national mode, pass national digits. The whole string is read as national input there, so a stored E.164 value like '+14155550132' comes out as '1 (415) 555-0132'. Prefill international inputs from E.164 directly.

Each call pushes one history entry, so a single undo() reverts the whole replacement. A call that leaves both the value and the region unchanged collapses into the current entry instead of creating an undo step.

setRegion(region) switches the active region, re-resolves the current digits under its rules, and pushes one history entry. In national mode it changes the formatting convention:

// mode: 'national', defaultRegion: 'US'
phoneInput.setRegion('GB');
phoneInput.setValue('2071838750');
phoneInput.getState().value; // '20 7183 8750'
phoneInput.getPhoneNumber().formatE164(); // '+442071838750'

In international mode with the calling code in the value, the digits decide the region, and setRegion matters only where they cannot: with '+44 20 7183 8750' in the input, setRegion('UA') changes nothing, but with only '+1 ' typed, setRegion('CA') resolves the shared calling code to 'CA' instead of the default 'US'.

Input and picker sync shows setRegion driven by a region picker in both directions.

The adapter already routes the platform’s own gestures through the controller history, so every undo step restores a full controller state: value, region, and caret. The browser’s native text undo never runs against the controlled value.

Gesture Handling
Ctrl+Z or Cmd+Z undo()
Ctrl+Shift+Z, Cmd+Shift+Z, Ctrl+Y, or Cmd+Y redo()
Native historyUndo and historyRedo input events, for example the browser’s Edit menu undo() and redo()

undo() and redo() expose the same steps to your own UI, for example toolbar buttons. Methods and gestures alike are no-ops when no step is available. canUndo() and canRedo() are methods on the instance, not fields of PhoneInputState, so read them inside your subscriber:

phoneInput.subscribe(() => {
undoButton.disabled = !phoneInput.canUndo();
redoButton.disabled = !phoneInput.canRedo();
});

Every editing operation and every setValue or setRegion call is one entry. The stack keeps up to maxHistorySize entries (default 150), dropping the oldest first, and editing after an undo discards the redo branch.

phoneInput.setValue('+14155550132'); // '+1 415-555-0132'
phoneInput.setValue('+442071838750'); // '+44 20 7183 8750'
phoneInput.undo();
phoneInput.getState().value; // '+1 415-555-0132'
phoneInput.canRedo(); // true

clearHistory() drops every undo and redo entry, keeping only the current state. Call it after programmatic prefill so the user’s first undo gesture edits their own input instead of reverting the form to empty:

phoneInput.setValue('+380501234567');
phoneInput.clearHistory();
phoneInput.canUndo(); // false
phoneInput.getState().value; // '+380 50 123 4567'

getState() returns the current PhoneInputState without subscribing. getPhoneNumber() builds a PhoneNumber from the current digits, the same interface parsePhoneNumber returns:

const phoneNumber = phoneInput.getPhoneNumber();
if (phoneNumber.isValid()) {
phoneNumber.formatE164(); // '+380501234567'
}

Validate on submit covers the submit-time checks built on this object.

destroy() detaches the DOM listeners and clears all subscribers. It is idempotent, and it does not clear the element: the last formatted value stays in the DOM.

One PhoneInput attaches per element; a second createPhoneInput on the same element throws. destroy() releases the element, so a later createPhoneInput may attach a fresh instance to it. A destroyed instance never writes to the element or notifies subscribers again; discard it rather than reusing it.

In component frameworks, create the instance when the element mounts and call destroy() in the unmount hook; the frameworks guide covers component lifecycles.