Toast Notifications
Keep on Learning!
If you liked what you've learned so far, dive in! Subscribe to get access to this tutorial plus video, code and script downloads.
With a Subscription, click any sentence in the script to jump to that part of the video!
Login SubscribeAn important part of any functional beautiful site is a notification system. In Symfony, we often think of flash messages: messages that we render near the top of the page, for example, after the user submits a form. And yes, that is what I'm talking about. But just rendering them at the top of the page isn't good enough for us. Instead, I want to render them as rich, toast-style notifications in the upper right that disappear automatically with CSS transitions and can tie my shoes for me.
Rendering Flash Messages
On our CRUD controllers, I'm already setting a success flash message... but I'm not rendering it anywhere. In the templates/ directory, create a new _flashes.html.twig. To start, just loop over the success messages with for message in app.flashes('success')... and endfor:
| {% for message in app.flashes('success') %} | |
| // ... lines 2 - 4 | |
| {% endfor %} |
Inside, I'll paste a very simple flash message, which will start fixed to the bottom of the page:
| {% for message in app.flashes('success') %} | |
| <div class="fixed bottom-0 right-0 m-4 p-4 bg-green-500 text-white rounded shadow"> | |
| {{ message }} | |
| </div> | |
| {% endfor %} |
Next, in base.html.twig, instead of rendering the flashes somewhere near the top of the body, put them at the bottom. Say <div id="flash-container"> then {{ include('_flashes.html.twig') }}:
| <html> | |
| // ... lines 3 - 15 | |
| <body class="bg-black text-white font-mono"> | |
| // ... lines 17 - 51 | |
| <div id="flash-container"> | |
| {{ include('_flashes.html.twig') }} | |
| </div> | |
| </body> | |
| </html> |
The id="flash-container" isn't important yet, but it will be useful later when we talk about Turbo streams.
So let's see if this works! Hit "Save" and... there we go! It's in a weird spot, but it shows up.
Making the Notification Pretty!
To make this look nicer, let's take a trip to Flowbite. Search for "toast". Ah, this has some great examples for different styles of toast notifications. This has me feeling dangerous!
Tip
I also recommend adding a data-turbo-temporary attribute to the root <div>.
This will remove the flash message before Turbo takes its "snapshot" for caching,
This means that if the user clicks "Back" to a page, the toast won't still be visible.
Back in _flashes.html.twig, I'll paste in some content:
| {% for message in app.flashes('success') %} | |
| <div | |
| class="fixed top-5 right-5 flex items-center w-full max-w-xs p-4 mb-4 text-gray-500 bg-white rounded-lg shadow dark:text-gray-400 dark:bg-gray-800" | |
| role="alert" | |
| > | |
| <div class="inline-flex items-center justify-center flex-shrink-0 w-8 h-8 text-green-500 bg-green-100 rounded-lg dark:bg-green-800 dark:text-green-200"> | |
| <svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20"> | |
| <path d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5Zm3.707 8.207-4 4a1 1 0 0 1-1.414 0l-2-2a1 1 0 0 1 1.414-1.414L9 10.586l3.293-3.293a1 1 0 0 1 1.414 1.414Z"/> | |
| </svg> | |
| <span class="sr-only">Check icon</span> | |
| </div> | |
| <div class="ms-3 text-sm font-normal">{{ message }}</div> | |
| <button | |
| type="button" | |
| class="ms-auto -mx-1.5 -my-1.5 bg-white text-gray-400 hover:text-gray-900 rounded-lg focus:ring-2 focus:ring-gray-300 p-1.5 hover:bg-gray-100 inline-flex items-center justify-center h-8 w-8 dark:text-gray-500 dark:hover:text-white dark:bg-gray-800 dark:hover:bg-gray-700" | |
| aria-label="Close" | |
| > | |
| <span class="sr-only">Close</span> | |
| <svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14"> | |
| <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"/> | |
| </svg> | |
| </button> | |
| </div> | |
| {% endfor %} |
This is heavily inspired by the Flowbite examples. But nothing really changed: we're still looping over the same collection and still dumping out the message. We've just got nice markup around this.
And I can't want to see it! I'll go to edit and "Save". Oh, that is wonderful! In the upper right where I want it and all done with CSS.
Making the Toast Closeable
Though, it doesn't auto close yet. Heck, it doesn't close at all! Since "closing" things will be a common problem, let's create a reusable Stimulus controller that can do that.
In assets/controller/, add a new closeable_controller.js. I'll cheat and grab the code from another controller... clear it out... then add a close() method. When this is called, it'll remove the entire element that the controller is attached to:
| import { Controller } from '@hotwired/stimulus'; | |
| export default class extends Controller { | |
| close() { | |
| this.element.remove(); | |
| } | |
| } |
To use this, in _flashes.html.twig, attach the controller to the top level element because that's what should be removed on close. Then, down on the button, say data-action="closeable#close":
| {% for message in app.flashes('success') %} | |
| <div | |
| // ... lines 3 - 4 | |
| data-controller="closeable" | |
| > | |
| // ... lines 7 - 13 | |
| <button | |
| // ... lines 15 - 17 | |
| data-action="closeable#close" | |
| > | |
| // ... lines 20 - 23 | |
| </button> | |
| </div> | |
| {% endfor %} |
We don't need the click because this is a button, so Stimulus already knows that we want this to trigger on the click event.
Let's try it! Hit edit and Save. It's there... it's gone.
In just a few minutes of work, we created a beautiful and functional toast notification system! But, darn it, this is not cool enough for our 30 Days of LAST Stack mission! So tomorrow, we'll fancy-ify this with auto-close, CSS transitions and an animated timer bar.
10 Comments
The problem with "code blocks" (and with this comment I refer to Flowbite Toast component) is that it is difficult to find.
I expect to find separately, maybe in a comment (as done by ToG for first version of _flashes.html.twig ), but there is not.
So my second idea is to search in "Course code"... but where? There are 2 folders: "start" and "finish". I naturally go inside "start" because I think that "start" is the "actual-state-of-code-at-the-start-of-the-lesson".
Moreover I am not sure that in "finish" directory contains the current version of the script I need. I am not sure, for example, that content of
_flashes.html.twigis the same as in the video.Let's say the
_flashes.htm.twigwill be also modified in lesson 23, the question is: which version of that file will I find in "finish" directory? Version in lesson 16 or the modified one in lesson 23?Cheers
Danilo
Hey @Fedale
So about "Course code" you see there 2 directories
start/- the start point of selected course its like a PRE chapter 1 statefinish/- the end point e.g. what you will get after passing all chaptersand the current version of any file you can get from latest added code block related to that file, so for
_flashes.html.twigit will be that link https://symfonycasts.com/screencast/last-stack/toast#codeblock-035447cbfc and you can expand it to show all lines of codeCheers!
Just to add a tiny note: copying relevant code for
_flashes.html.twigcontained in "finish" directory, I've added alsodata-closeable-auto-close-value="5000"... but this attirbute is exaplined in the next lesson!Hello,
It's kind of a pain in the butt when huge blocks of code are pasted and the pasted code is not in the "script". :(
Would anyone be so nice to fix it?
Cheers,
Ju
Hey Julien,
I'm sorry about that, my bad! It's still on my list, will add it till the end of the day I think.
Cheers!
Lol thanks @Victor
Hey Julien,
No problem! Code blocks may come out with some delays, but eventually all chapters will have code blocks :)
Cheers!
My flash messages are appearing off the bottom of the page, but then I can't see all the classes you're using in the exmaple. Please can someone share the actual content of _flashes.html.twig at this point, as the finihsed version is a lot more complex?
Ignore me. Fixed it. Was because I'd forgotten to run
./bin/console tailwind:build -wsince restaring my laptop.The fill code flashes up very briefly and is…
Hey ToG,
Yeah, something to remember... and also a good idea to try to restart it first if you have any weird behavior :)
Thanks for sharing the solution with others!
Cheers!
"Houston: no signs of life"
Start the conversation!