Objectifs

Dans ce court article nous allons découvrir la librairie Notyf, et comment la mettre en place dans un projet Symfony. A l’issue de ces quelques exemples, nous serons capable d’emettre de belles notifications, et de les gérer facilement.

Création d’un “mini projet” Symfony

Histoire d’avoir un support à notre démonsration, nous allons créer un projet Symfony.

  • Création du projet et mise en place de Webpack Encore.
symfony new notyf --webapp
cd notyf
npm install
npm run build
symfony serve -d
  • Création d’un Controlleur
symfony console make:controller NotyfController
  • Modifions notre Controlleur pour pouvoir lui passer des paramètres par son URL
<?php

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

class NotyfController extends AbstractController
{
    #[Route('/{notyf}', name: 'app_notyf')]
    public function index(?string $notyf = null): Response
    {
        return $this->render('notyf/index.html.twig', [
            'notyf' => $notyf,
        ]);
    }
}
  • Supprimons le contenu du fichier assets/styles/app.css et lançons le mode npm run watch

  • Modifions notre fichier templates/base.html.twig pour y utiliser pico.css

<!DOCTYPE html>
<html data-theme="dark">
    <head>
        <meta charset="UTF-8">
        <title>{% block title %}Welcome!{% endblock %}</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="https://unpkg.com/@picocss/pico@latest/css/pico.min.css">
        <link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 128 128%22><text y=%221.2em%22 font-size=%2296%22>⚫️</text></svg>">
        {# Run `composer require symfony/webpack-encore-bundle` to start using Symfony UX #}
        {% block stylesheets %}
            {{ encore_entry_link_tags('app') }}
        {% endblock %}

        {% block javascripts %}
            {{ encore_entry_script_tags('app') }}
        {% endblock %}
    </head>
    <body>
        <main class="container">
            {% block body %}{% endblock %}
        </main>
    </body>
</html>
  • Modifions également notre fichier templates/notyf/index.html.twig pour pouvoir afficher notre message et un petit menu.
{% extends 'base.html.twig' %}

{% block title %}Notyf 🚀
{% endblock %}

{% block body %}
	<nav>
		<ul>
			<li>
				<strong>Notyf 🚀</strong>
			</li>
		</ul>
		<ul>
			<li><a href="{{ path('app_notyf', {'notyf': 'success'}) }}" role="button">Success</a></li>
            <li><a href="{{ path('app_notyf', {'notyf': 'info'}) }}" role="button">Info</a></li>
            <li><a href="{{ path('app_notyf', {'notyf': 'warning'}) }}" role="button">Warning</a></li>
            <li><a href="{{ path('app_notyf', {'notyf': 'error'}) }}" role="button">Error</a></li>
		</ul>
	</nav>

    <article>
        <h1>{{ notyf }}</h1>
    </article>

{% endblock %}

Installation de la librairie Notyf et mise en place avec Webpack Encore

  • Installons la librairie Notyf
npm i notyf
  • Créons un fichier assets/notyf.js et y mettons le code suivant (pour tester !) :
import { Notyf } from 'notyf';
import 'notyf/notyf.min.css'; // for React, Vue and Svelte

// Create an instance of Notyf
const notyf = new Notyf();

// Display an error notification 
notyf.error('Please fill out the form');
  • Puis, apellons notre fichie dans la configuration de Webpack Encore webpack.config.js
// ...
.addEntry('app', './assets/app.js')
.addEntry('notyf', './assets/notyf.js')
// ...
  • Et enfin, utilons notre notyf dans notre fichier templates/base.html.twig
{% block stylesheets %}
    {{ encore_entry_link_tags('app') }}
    {{ encore_entry_link_tags('notyf') }
{% endblock %
{% block javascripts %}
    {{ encore_entry_script_tags('app') }}
    {{ encore_entry_script_tags('notyf') }}
{% endblock %}

Amélioration de notre gestion des notifications

  • Améliorons notre fichier assets/notyf.js
import { Notyf } from 'notyf';
import 'notyf/notyf.min.css';

// Create an instance of Notyf
const notyf = new Notyf({
    duration: 5000,
    position: {
        x: 'right',
        y: 'top',
    },
    types: [
        {
            type: 'info',
            background: '#00bfff',
            icon: false
        },
        {
            type: 'warning',
            background: '#ffd700',
            icon: false
        },
    ]
});

let messages = document.querySelectorAll('#notyf-message');

messages.forEach(message => {
    if (message.className === 'success') {
        notyf.success(message.innerHTML);
    }

    if (message.className === 'error') {
        notyf.error(message.innerHTML);
    }

    if (message.className === 'info') {
        notyf.open({
            type: 'info',
            message: '<b>Info</b> - ' + message.innerHTML,
        });
    }

    if (message.className === 'warning') {
        notyf.open({
            type: 'warning',
            message: '<b>Warning</b> - ' + message.innerHTML
        });
    }
});
  • Puis adaptons notre fichier templates/notyf/index.html.twig
{% extends 'base.html.twig' %}

{% block title %}Notyf 🚀
{% endblock %}

{% block body %}
	<nav>
		<ul>
			<li>
				<strong>Notyf 🚀</strong>
			</li>
		</ul>
		<ul>
			<li><a href="{{ path('app_notyf', {'notyf': 'success'}) }}" role="button">Success</a></li>
            <li><a href="{{ path('app_notyf', {'notyf': 'info'}) }}" role="button">Info</a></li>
            <li><a href="{{ path('app_notyf', {'notyf': 'warning'}) }}" role="button">Warning</a></li>
            <li><a href="{{ path('app_notyf', {'notyf': 'error'}) }}" role="button">Error</a></li>
		</ul>
	</nav>

    <article>
        <h1>{{ notyf }}</h1>
    </article>

    <div id="notyf-message" class={{ notyf }}>{{ notyf }}</div>

{% endblock %}

Refactoring de notre code

  • Créons un fichier templates/_notyf.html.twig
{% for message in app.flashes('success') %}
    <div id="notyf-message" class="success">{{ message }}</div>
{% endfor %}

{% for message in app.flashes('info') %}
    <div id="notyf-message" class="info">{{ message }}</div>
{% endfor %}

{% for message in app.flashes('warning') %}
    <div id="notyf-message" class="warning">{{ message }}</div>
{% endfor %}

{% for message in app.flashes('error') %}
    <div id="notyf-message" class="error">{{ message }}</div>
{% endfor %}
  • Appellons ce fichier dans notre fichier templates/base.html.twig
<body>
    <main class="container">
        {% include '_notyf.html.twig' %}
        {% block body %}{% endblock %}
    </main>
</body>
  • Nettoyage de notre fichier templates/notyf/index.html.twig
{% extends 'base.html.twig' %}

{% block title %}Notyf 🚀
{% endblock %}

{% block body %}
	<nav>
		<ul>
			<li>
				<strong>Notyf 🚀</strong>
			</li>
		</ul>
		<ul>
			<li><a href="{{ path('app_notyf', {'notyf': 'success'}) }}" role="button">Success</a></li>
            <li><a href="{{ path('app_notyf', {'notyf': 'info'}) }}" role="button">Info</a></li>
            <li><a href="{{ path('app_notyf', {'notyf': 'warning'}) }}" role="button">Warning</a></li>
            <li><a href="{{ path('app_notyf', {'notyf': 'error'}) }}" role="button">Error</a></li>
		</ul>
	</nav>

    <article>
        <h1>{{ notyf }}</h1>
    </article>

{% endblock %}
  • Adapatons notre controlleur src/Controller/NotyfController.php pour utiliser le sytème de “Flash message” de Symfony
class NotyfController extends AbstractController
{
    #[Route('/{notyf}', name: 'app_notyf')]
    public function index(?string $notyf = null): Response
    {
        if (null != $notyf) {
            $this->addFlash($notyf, 'This is a '.$notyf.' flash message.');
        }

        return $this->render('notyf/index.html.twig', [
            'notyf' => $notyf,
        ]);
    }
}
  • Et enfin, cachons le message en dehors du toast en ajoutant un bout de css dans assets/styles/app.css
#notyf-message {
    display: none;
}
  • Apprecions le résultat :)

Code source