Masking Livewire inputs with manny

How to create masks for livewire inputs using hallindavid/manny package

Mar 30, 2020

This post expects you to be comfy with Laravel development, and a bit of Livewire as well - although I do go through setting up livewire.

Creating the Project

So let’s start off by creating our project and stepping into it.

laravel new livewiremask
cd livewiremask

Pull in scaffolding and livewire

Let’s pull in Livewire and also pull in the ui front-end scaffolding - just so we don’t have to build much in terms of a theme.

composer require livewire/livewire
composer require laravel/ui 
php artisan ui bootstrap --auth
npm install && npm run dev

Although we really don’t need the the auth - I just want to steal the register form for this example.

At this point, you’ll need to set up the dev environment for however you use Laravel normally. For me, it’s valet.

valet link

open your browser window and check out what we’ve done so far. You should be greeted with the laravel default landing page

Setup Livewire Component

So the first thing we need to do is add the livewire assets into the layout.

So let’s go to resources/view/layouts/app.blade.php and add in our styles + scripts

styles in the head

	...
	<!-- Styles -->
    <link href="{{ asset('css/app.css') }}" rel="stylesheet">

    //add this line
    <livewire:styles>
</head>

scripts just before the end of the body tag

		...
        <main class="py-4">
            @yield('content')
        </main>
    </div>

    //add this line
    <livewire:scripts>
</body>

Create the Livewire Component

Next up we’re going to create the livewire component, and create a route to serve the livewire component.

php artisan make:livewire maskedform

This should have created 2 new files for you.
app/Http/Livewire/MaskedForm.php This is the component code, and
resources/views/livewire/maskedform.blade.php this is the component view.

Let’s also create the view livewireform.blade.php in the resources/views folder. This view will hold our livewire component for us

@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-body">
                	<livewire:maskedform>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

Let’s create a route to the livewire component by adding this line into routes/web.php

Route::get('/maskedform', function() {
	return view ('livewireform');
});

and finally let’s add this code to to our livewire component view

<form>
    <div class="form-group row">
        <label class="col-md-4 col-form-label text-md-right">Phone</label>
        <div class="col-md-6">
            <input type="text" class="form-control" autofocus>
        </div>
    </div>
</form>

Now if you browse to the route, (with valet, mine is livewiremask.test/maskedform ) you should see this

Ok - so now we can start defining the different variables we want to play with

Defining the Livewire Variables

Let’s jump into livewire code, app/Http/Livewire/Maskedform.php. It should look like this:

<?php

namespace App\Http\Livewire;

use Livewire\Component;

class Maskedform extends Component
{
    public function render()
    {
        return view('livewire.maskedform');
    }
}

The first thing we want to do is bind the phone field we created in the view to a variable in the code.

class Maskedform extends Component
{
	public $phone;

    public function updated($field)
   	{
   		if ($field == 'phone') {
   			//this is where we will detect any changes to the phone field.
   		}
    }
    ...

and in the livewire view, we are going to bind it by adding the wire:model="phone" to the input.

<div class="form-group row">
    <label for="phone" class="col-md-4 col-form-label text-md-right">Phone</label>
    <div class="col-md-6">
        <input wire:model="phone" id="phone" type="text" class="form-control" autofocus>
    </div>
</div>

Pull in Manny - start masking

now it’s time to pull in Manny - (manny is short for manipulator)

composer require hallindavid/manny

The docs for Manny are available here: github.com/hallindavid/manny there are quite a few little functions that are super helpful with Livewire.

The first mask we’ll play with is, just to eliminate all non-numeric characters. You can do through using preg_replace with regular expressions, but Manny has a function called Stripper which can also do this for us without the need for remembering how to write regular expressions.

In our app/Http/Livewire/Maskedform.php file, let’s update our code

use Livewire\Component;
use Manny;

class Maskedform extends Component
{
	public $phone;

	public function updated($field)
	{
		if ($field == 'phone')
		{
			$this->phone = Manny::stripper($this->phone, ['num']);
		}
	}


    public function render()
    ...

Now if you go back to your browser and play with the phone field, you should see this happening:

Well that’s great for stripping all the non-numeric characters out, but what if you want to format it in a specific way? In Canada/US we have 10 digit phone numbers in most places, and usually people don’t include the country code on the number. So you see phone numbers that look like this: (987) 123-4567 or 987-123-4567.

Let’s change the Component code to this:

if ($field == 'phone')
{
	$this->phone = Manny::mask($this->phone, "(111) 111-1111");
}

go back to your page, and you should be see this now:

For more advanced phone numbers there is actually a class - Manny\Phone that you can extend to do more advanced phone number support - with extensions and other special formatting.

What about other types of fields? Another great example of using the Manny::mask function is for canadian postal codes. Canadian postal codes are 2 groups of 3 characters, each group alternates between alphbetical and numeric, so like this: A1A 1A1

In the view, let’s create a seperate input for the postal code.

<form>
    <div class="form-group row">
        <label class="col-md-4 col-form-label text-md-right">Phone</label>
        <div class="col-md-6">
            <input wire:model="phone" type="text" class="form-control" autofocus>
        </div>
    </div>
    <div class="form-group row">
        <label class="col-md-4 col-form-label text-md-right">Postal Code</label>
        <div class="col-md-6">
            <input wire:model="postal" type="text" class="form-control" autofocus>
        </div>
    </div>
</form>

and let’s also make the variable in the code

public $phone;
	public $postal;

	public function updated($field)
	{
		if ($field == 'postal')
		{
			$this->postal = strtoupper(Manny::mask($this->postal, "A1A 1A1"));
		}
		...

Go ahead and reload - you should see this now.

The mask function is pretty nifty for doing a lot of different patterns. some other ones to try would be

$this->birthday = Manny::mask($this->birthday, "11/11/1111");
$this->date = Manny::mask($this->date, "1111-11-11");
$this->time = Manny::mask($this->time, "11:11");

Note on masking with Livewire

Livewire is very fast, but due to the fact that is being sent through ajax, processed in PHP then returned in javascript, the default debounce may not be good enough for some masks. Some tips to deal with this could be:

Increase the debounce

<input type="text" wire:model.debounce.500ms="phone">

Make the inputs lazy (masks after the user has left the input field)

<input type="text" wire:model.lazy="phone">

Source Code

To view the source code from this example go to the GitHub repo hallindavid/livewire-masks-with-manny