Skip to content

Add .display modifier to x-mask#4853

Open
joshhanley wants to merge 16 commits into
mainfrom
josh/x-mask-display
Open

Add .display modifier to x-mask#4853
joshhanley wants to merge 16 commits into
mainfrom
josh/x-mask-display

Conversation

@joshhanley

@joshhanley joshhanley commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator

The Scenario

A masked input may need to display a formatted value while keeping an unformatted value in application state.

For example, this Livewire property should remain the integer 1000, while the input displays 1,000. Instead, Livewire receives the formatted string "1,000" for the integer $quantity property, causing a property type error.

CleanShot 2026-07-13 at 18 04 33
<?php

use Livewire\Component;

new class extends Component {
    public int $quantity = 1000;

    public function updating(): void
    {
        usleep(500_000);
    }

    public function setQuantity(): void
    {
        $this->quantity = 2000;
    }
};
?>

<div>
    <flux:input
        label="Quantity"
        type="text"
        x-mask:dynamic="$money($input)"
        wire:model.live="quantity"
    />

    <flux:button wire:click="setQuantity">Set to 2000</flux:button>

    <div>
        <flux:label>Livewire value</flux:label>
        <flux:text>{{ $quantity }}</flux:text>
    </div>
</div>

The Problem

x-mask does not distinguish between the value displayed in the input and the value stored in the model.

When 1000 is masked to 1,000, the formatted value replaces the input’s value. It is then picked up by x-model and sent to Livewire, which attempts to store the string "1,000" instead of the integer 1000, triggering the property type error.

The Solution

This PR adds a new .display modifier to x-mask.

The modifier separates the value displayed in the input from the value stored in the model. The masked value is assigned to the input, while x-model receives the unmasked value.

This PR also adds _x_modelValue support to x-model. Display masks use it to expose the unmasked value. When present, x-model uses this value instead of the input's value.

In the example above, adding .display makes the input display 1,000 while $quantity remains the integer 1000:

<flux:input
    label="Quantity"
    type="text"
    x-mask:dynamic.display="$money($input)"
    wire:model.live="quantity"
/>
CleanShot 2026-07-13 at 18 06 37

Dynamic masks now remove obsolete separators after backspacing.

Without .display, x-mask continues to store the formatted value in the model.

Fixes livewire/flux#2666

@joshhanley joshhanley changed the title Add x-mask.display modifier Add x-mask.display modifier Jul 1, 2026
@asintadouno5-hue

Copy link
Copy Markdown

Run

@joshhanley

Copy link
Copy Markdown
Collaborator Author

@asintadouno5-hue what?

@joshhanley joshhanley changed the title Add x-mask.display modifier Add .display modifier to x-mask Jul 13, 2026
@joshhanley
joshhanley marked this pull request as ready for review July 13, 2026 08:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Mask on input triggers unwanted update hooks

2 participants