Make a password display with Symfony’s FormType when you press your eyes
Common
Just add it to formBuilder
as shown below and create a FormType that will be like ↑.
$formBuilder()->add('password', ShowHidePasswordType::class);
#How to make
Project creation
# sensio/framework-extra-bundle is not supported by the latest version of symfony, but somehow it is installed by default, so deleted
$ symfony new show_hide_password --full
$ composer remove sensio/framework-extra-bundle
Base form screen creation
Create a Controller with the following command.
$ bin/console make:controller
Choose a name for your controller class (e.g. TinyGnomeController):
> home
created: src/Controller/HomeController.php
created: templates/home/index.html.twig
Create a form by rewriting Controller and template as follows.
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Routing\Annotation\Route;
class HomeController extends AbstractController
{
/**
* @Route("/home", name="home")
*/
public function index()
{
$form = $this->createFormBuilder()
->add('username', TextType::class)
->add('password', PasswordType::class)
->add('submit', SubmitType::class)
->getForm()
;
return $this->render('/home/index.html.twig', [
'form' => $form->createView()
]);
}
}
{% extends'base.html.twig' %}
{% block title %}Hello HomeController!{% endblock %}
{% block body %}
<div class="container mt-5">
<div class="row">
<div class="col-8 offset-2">
<div class="card">
<div class="card-body">
{{ form_start(form) }}
{{ form_rest(form) }}
{{ form_end(form) }}
</div>
</div>
</div>
</div>
</div>
{% endblock %}
Also, since it uses the bootstrap form theme, add a CDN link to the activation settings & template.
twig:
default_path:'%kernel.project_dir%/templates'
form_theme: ['bootstrap_4_layout.html.twig'] # Added
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{% block title %}Welcome!{% endblock %}</title>
{% block stylesheets %}
// from here
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9ZwR1T2JwR1T2JJR1 ="anonymous">
// So far
{% endblock %}
</head>
<body>
{% block body %}{% endblock %}
{% block javascripts %}
// from here
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4s0861IHNDGainJNDZwrnQq4s086I" script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jan"mousorigin" crossorigin
// So far
{% endblock %}
</body>
</html>
Start the server with the following command and access https://127.0.0.1:8000 to display a page like the image.
$ symfony server:start -d
[OK] Web server listening
The Web server is using PHP FPM 7.4.9
https://127.0.0.1:8000
Make an example
First, create a FormType. The name is appropriate (laughs). This is a child Type of PasswordType. Controller should use ShowHidePasswordType instead of PasswordType.
<?php
namespace App\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
class ShowHidePasswordType extends AbstractType
{
public function getParent()
{
return PasswordType::class;
}
}
...
use App\Form\ShowHidePasswordType; // add
...
$form = $this->createFormBuilder()
->add('username', TextType::class)
//->add('password', PasswordType::class)
->add('password', ShowHidePasswordType::class) // add->add('submit', SubmitType::class)
->getForm()
;
...
Then create a custom form theme and change the settings to use this theme. Inherit bootstrap_4_layout.html.twig
and overwrite only the display method of ShowHidePasswordType.
{% extends'bootstrap_4_layout.html.twig' %}
twig:
default_path:'%kernel.project_dir%/templates'
# form_theme: ['bootstrap_4_layout.html.twig']
form_theme: ['form_theme.html.twig'] # Added
By default, Symfony form themes use the {% block form_row %} ... {% endblock %}
block for rendering, but with ShowHidePasswordType
{% block show_hide_password %} ... {% endblock If a block called %}
is defined, that block will be used preferentially for rendering. Please refer to the document 1 for detailed explanation of naming conventions.
The {% block form_row %} ... {% endblock %}
of bootstrap_4_layout.html.twig
is as follows. In this, the part {{-form_widget(form, widget_attr) -}}
is the part that actually renders the input element. So, wrap it here and try to display the example by clobbering with css or JavaScript.
```twig:bootstrap_4_layout.html.twig` {% block form_row -%} {%- if compound is defined and compound -%} {%- set element ='fieldset’ -%} {%- endif -%} {%- set widget_attr = {} -%} {%- if help is not empty -%} {%- set widget_attr = {attr: {‘aria-describedby’: id ~”_help”}} -%} {%- endif -%} <{{ element|default(‘div’) }}{% with {attr: row_attr|merge({class: (row_attr.class|default('') ~'form-group’)|trim})} %} {{ block(‘attributes’) }}{% endwith %}> {{- form_label(form) -}} {{- form_widget(form, widget_attr) -}} # Coco {{- form_help(form) -}} </{{ element|default(‘div’) }}> {%- endblock form_row %}
Here is what was finally completed. I don't do much because I just wrap the input element and the eye icon in a div and display it nicely with `position:absolute`. When the eye icon is clicked, the input type is changed with JavaScript.
The eye icon uses the fontawesome resource, so add the CDN to `base.html.twig`. If you reload the browser, you will see the common thing like the beginning.
```twig:templates/form_theme.html.twig
{% extends'bootstrap_4_layout.html.twig' %}
{% block show_hide_password_row %}
<style>
.showHiddenPassword-wrapper {
position: relative;
}
.showHiddenPassword-wrapper .is-invalid {
background-image: none!important;
background-size: 0!important;
}
.showHiddenPassword-toggle {
position: absolute;
top: 50%;
right: 1.5em;
transform: translateY(-50%);
}
</style>
<script>
function __togglePassword__{{ form.vars.id }}() {
const _passwordField = document.querySelector('#{{ form.vars.id }}');
const _showHideToggle = document.querySelector('#showHideToggle-{{ form.vars.id }}');
if (_showHideToggle.classList.contains('fa-eye-slash')) (
_showHideToggle.classList.remove('fa-eye-slash')
_showHideToggle.classList.add('fa-eye')
_passwordField.type ='text'
} else {
_showHideToggle.classList.remove('fa-eye')
_showHideToggle.classList.add('fa-eye-slash')
_passwordField.type ='password'
}
}
</script>
{%- if compound is defined and compound -%}
{%- set element ='fieldset' -%}
{%- endif -%}
{%- set widget_attr = {} -%}
{%- if help is not empty -%}
{%- set widget_attr = {attr: {'aria-describedby': id ~"_help",'class':'showHiddenPassword-widget'}} -%} {# class added #}
{%- endif -%}
<{{ element|default('div') }}{% with {attr: row_attr|merge({class: (row_attr.class|default('') ~'form-group')|trim})} %} {{ block('attributes') }}{% endwith %}>
{{- form_label(form) -}}
{# from here #}
<div class='showHiddenPassword-wrapper'>
{{- form_widget(form, widget_attr) -}}
<span class='showHiddenPassword-toggle'
onclick='__togglePassword__{{ form.vars.id }}()'
>
<i id='showHideToggle-{{ form.vars.id }}' class="fa fa-eye-slash"></i>
</span>
</div>
{# So far #}
{{- form_help(form) -}}
</{{ element|default('div') }}>
{% endblock %}
...
{% block stylesheets %}
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9ZwR1T2JwR1T2JJR1 ="anonymous">
// from here
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.13.0/css/all.min.css" rel="stylesheet" crossorigin="anonymous">
// So far
{% endblock %}
...
Of course, it’s better to define CSS and JavaScript in separate files. However, in that case, it takes time to read that file, so if you want to complete with only FormType, I personally think that it is ant to do it like this. (How about that)
This time source code
https://github.com/ggg-mzkr/show_hide_password