Courses

Practical Livewire 3: Order Management System Step-by-Step

Register Password Show/Hide/Strength

We will start this course, by making the register page better. Here we will add a button to show/hide the password, generate a random password, and will show the password strength bar.

finished passwords component


New Livewire Component

First, we will create a Livewire component.

php artisan make:livewire RegisterPasswords

Now, we need to move password fields to the newly created component, and instead call our created Livewire component.

resources/views/livewire/register-passwords.blade.php:

<div>
<!-- Password -->
<div class="mt-4">
<x-input-label for="password" :value="__('Password')" />
 
<x-text-input id="password" class="block mt-1 w-full"
type="password"
name="password"
required autocomplete="new-password" />
 
<x-input-error :messages="$errors->get('password')" class="mt-2" />
</div>
 
<!-- Confirm Password -->
<div class="mt-4">
<x-input-label for="password_confirmation" :value="__('Confirm Password')" />
 
<x-text-input id="password_confirmation" class="block mt-1 w-full"
type="password"
name="password_confirmation" required />
 
<x-input-error :messages="$errors->get('password_confirmation')" class="mt-2" />
</div>
</div>

resources/views/auth/register.blade.php:

@livewire('register-passwords')

Show/Hide Password with Alpine.js

First, let's make a password show/hide feature. For this, we only will use Alpine.js, no Livewire needed, as we interact with the browser only, no need to send the data to the server.

If you're new to Alpine.js, you can watch this free 7-minute quick introduction video and it will be enough for you to understand, for the purpose of this tutorial.

resources/views/livewire/register-passwords.blade.php:

<x-text-input id="password" class="block mt-1 w-full"
type="password"
name="password"
required autocomplete="new-password" />
 
<div class="flex mt-1 mb-2">
<div class="relative flex-1 col-span-4" x-data="{ show: true }">
<input class="w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50"
id="password"
:type="show ? 'password' : 'text'"
name="password"
required autocomplete="new-password" />
 
<button type="button" class="flex absolute inset-y-0 right-0 items-center pr-3" @click="show = !show" :class="{'hidden': !show, 'block': show }">
<!-- Heroicon name: eye -->
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M2.036 12.322a1.012 1.012 0 010-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178z" />
<path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
</button>
<button type="button" class="flex absolute inset-y-0 right-0 items-center pr-3" @click="show = !show" :class="{'block': !show, 'hidden': show }">
<!-- Heroicon name: eye-slash -->
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M3.98 8.223A10.477 10.477 0 001.934 12C3.226 16.338 7.244 19.5 12 19.5c.993 0 1.953-.138 2.863-.395M6.228 6.228A10.45 10.45 0 0112 4.5c4.756 0 8.773 3.162 10.065 7.498a10.523 10.523 0 01-4.293 5.774M6.228 6.228L3 3m3.228 3.228l3.65 3.65m7.894 7.894L21 21m-3.228-3.228l-3.65-3.65m0 0a3 3 0 10-4.243-4.243m4.242 4.242L9.88 9.88" />
</svg>
</button>
</div>
</div>

Here are a few things to explain.

Everything in Alpine starts with the x-data directive, so we add reactive data called show and by default, we set it to true. Next, for the input, we bind the type attribute to change its type to password or text.

We have two buttons for which we use heroicons to show icons. For those buttons, we use the shorthand syntax @click to change the show status. And the last thing for buttons: we bind class to change whether show them or not.

password show hide


Generate Random Password

Now, let's add a button to generate a random password. For this, we need two public properties on the Livewire component, to bind both password fields.

app/Livewire/RegisterPasswords.php:

class RegisterPasswords extends Component
{
public string $password = '';
 
public string $passwordConfirmation = '';
}

resources/views/livewire/register-passwords.blade.php:

<input class="w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50"
id="password"
:type="show ? 'password' : 'text'"
name="password"
wire:model="password"
required autocomplete="new-password" />
 
<x-text-input id="password_confirmation" class="block mt-1 w-full"
type="password"
wire:model="passwordConfirmation"
name="password_confirmation" required />

Next, let's add the Generate button itself.

resources/views/livewire/register-passwords.blade.php:

<div class="flex mt-1 mb-2">
<div class="relative flex-1 col-span-4" x-data="{ show: true }">
...
</div>
<div class="flex items-center place-content-end ml-1">
<x-primary-button wire:click="generatePassword" type="button">Generate</x-primary-button>
</div>
</div>

generate password button

This new button has a Livewire action called generatePassword.

app/Livewire/RegisterPasswords.php:

use Illuminate\Support\Str;
 
class RegisterPasswords extends Component
{
public string $password = '';
 
public string $passwordConfirmation = '';
 
public function generatePassword(): void
{
$password = Str::password(12);
 
$this->setPasswords($password);
}
 
protected function setPasswords($value): void
{
$this->password = $value;
$this->passwordConfirmation = $value;
$this->updatedPassword($value);
}
 
public function render()
{
return view('livewire.register-passwords');
}
}

generated password


Show Password Strength

The last feature we will add to the registration form is to show password strength. To determine strength we will use bjeavons/zxcvbn-php package.

composer require bjeavons/zxcvbn-php

To update the strength value we will use Livewire Lifecycle Hook and will set the $strengthScore public property. Also, we need to update the strength score when generating passwords.

app/Http/Livewire/RegisterPasswords.php:

use ZxcvbnPhp\Zxcvbn;
 
class RegisterPasswords extends Component
{
public string $password = '';
 
public string $passwordConfirmation = '';
 
public int $strengthScore = 0;
 
public function updatedPassword($value)
{
$this->strengthScore = (new Zxcvbn())->passwordStrength($value)['score'];
}
 
// ...
 
protected function setPasswords($value): void
{
$this->password = $value;
$this->passwordConfirmation = $value;
$this->updatedPassword($value);
}

Now we can show the strength value in the blade file. Besides the progress bar, we will write in words what's the password strength, for example, "weak" or "strong".

Because we only have an integer value of the score, we will add a public property as array of which score value corresponds to which strength in words.

app/Http/Livewire/RegisterPasswords.php:

class RegisterPasswords extends Component
{
public string $password = '';
 
public string $passwordConfirmation = '';
 
public int $strengthScore = 0;
 
public array $strengthLevels = [
1 => 'Weak',
2 => 'Fair',
3 => 'Good',
4 => 'Strong',
];
 
// ...

And just before password validation, let's show the strength:

resources/views/livewire/register-passwords.blade.php:

</div>
<span class="text-sm">
<span class="font-semibold">Password strength:</span> {{ $strengthLevels[$strengthScore] ?? 'Weak' }}
</span>
 
<progress value="{{ $strengthScore }}" max="4" class="w-full"></progress>
 
<x-input-error :messages="$errors->get('password')" class="mt-2" />
</div>

show password strength

And that's it for this part, now we enhanced the registration form.

avatar

For the heroicon of the eye, the class did not adjust the position correctly. <button type="button" class="flex absolute inset-y-0 right-0 items-center pr-3"

			I changed inset-y-0 to top-0 and also adjusted padding a bit.
			
			For if it helps. Fresh installation of today Feb, 9th 2023
👍 3
avatar

If you run "npm run build" or "dev". It will be placed where it should.

avatar

Tks. It worked. Sorry for the inconvenience.

avatar

in register.blade.php it not clear where to paste the code

@livewire('register-passwords')

avatar

The content of register-passwords represented by the @livewire('register-passwords') replaces the Password and Confirm Password sections in the register.blade.php.

In general if you are not sure, just check the repo and compare.

avatar

Any idea where the repo would be hiding? I am two steps into this "tutorial" and already lost, I can't wait for the rest.

avatar

The link to the final repo is at the end of the final lesson :)

avatar

@Povilas Korop, could you kindly assign a number to each step so that I can easily reference a specific step in my comments on issue?

avatar

@Sylvain not sure what you mean by "assign a number to each step", what do you call a "step" here?

avatar

in step Show/Hide Password with Alpine.js I dont see any change. maybe missing some step ?

avatar

Well it worked for me, hard to comment on what didn't work for you. Maybe check with the repository and compare the code?

avatar

It seems that the current explanation is not clear enough for us to understand how it works. Perhaps some improvements are needed to make the explanation more understandable.

avatar

I got the following error while implementing Generate Random Password method:

App\Http\Middleware\RedirectIfAuthenticated::handle(): Return value must be of type Symfony\Component\HttpFoundation\Response, null returned

P.S. I am using Laravel 10 for this course.

👍 1
avatar

Found that Laravel 10 return types have broken the Livewire components that work with unauthenticated users. Livewire is planning to release the update for this, in upcoming days, hopefully: https://github.com/livewire/livewire/pull/5561

avatar

Dependencies make projects a bit fragile :-(

avatar

run composer update, https://github.com/livewire/livewire/commit/5ab6266a12c637f645d38a2f0f61f84182f6249d

avatar

Please upgrade the repository to Laravel 10. I'm stuck there! The livewire not generating a random password. Thanks!!

👍 1
avatar

I will re-check it with Laravel 10 next week, and make updates.

avatar

even me, im using laravel 10

avatar

In the repo laravel 10 is used. Saying doesn't generate doesn't help at all

avatar

@Povilas Korop I'm a programmer who has been using your teachings for a long time. I went back to studying livewire. Copy all files that have been modified or created so far from github up to here. I believe that some step is missing in this step by step. Could you please check if there is something wrong with this tutorial? The error I'm getting occurs when typing the first letter in the input password.

This is the error link: https://flareapp.io/share/lm2R1GAm#F51

avatar

Hard to answer without debugging. Maybe Laravel 10 changed something... Could you please put your code on GitHub and invite me (username PovilasKorop) and then I could debug next week.

avatar

@Rogerio Picilli The same issue that I am facing. Are you using Laravel 10?

avatar

Found that Laravel 10 return types have broken the Livewire components that work with unauthenticated users. Livewire is planning to release the update for this, in upcoming days, hopefully: https://github.com/livewire/livewire/pull/5561

avatar

@Rajesh Budhathoki yes I'm.

avatar

The issue is fixed with new Livewire release, run "composer update": https://github.com/livewire/livewire/releases/tag/v2.11.3

avatar

Now it's working, thank you so much.

avatar

@Povilas Korop thanks for your update. Lets wait for the livewire update.

avatar

@Povilas Korop it's working now.

avatar

Very nice, thanks

avatar

Thanks, i will use this topic in my application

avatar

Povilas inside your RegisterPasswords livewire component, why did you use underscore for your variable named $password_confirmation and camelCase for your variable named $strengthScore?

avatar

Well noted, it should be consistent with camelCase, I agree. Will be more careful next time :)

avatar

I only have one request as sort of an add-on When typing in a password it needs to also be simultaneously entered in to the password confirmation field. I know that this can be done because you have done it before.

avatar

Hi Richard dont get me wrong but I think It certainly can be done, but it totally misses the point of you having it. If you're going to auto-populate the field then you don't need it on the form, right?

avatar

In my videos, I have done it with Chrome add-on, if I understand it correctly. And I agree with Rogerio, what's the point of that field, then?

avatar

The strenght indicator works only when generating a password, not when you enter it manually. You need to add

public function updatePassword(){
    $this->updatedPassword($this->password);
}

and then add this to the password input:

wire:keydown="updatePassword"
👍 2