Chapters
32 Chapters
|
4:16:27
|
Login to bookmark this video
-
Course Code
Subscribe to download the code!
Subscribe to download the code!
-
This Video
Subscribe to download the video!
Subscribe to download the video!
-
Subtitles
Subscribe to download the subtitles!
Subscribe to download the subtitles!
-
Course Script
Subscribe to download the script!
Subscribe to download the script!
Scroll down to the script below, click on any sentence (including terminal blocks) to jump to that spot in the video!
Subscribe to jump to this part in the video!
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.
What PHP libraries does this tutorial use?
// composer.json
{
"require": {
"php": ">=8.2",
"ext-ctype": "*",
"ext-iconv": "*",
"babdev/pagerfanta-bundle": "4.x-dev", // 4.x-dev
"doctrine/doctrine-bundle": "^2.10", // 2.12.x-dev
"doctrine/doctrine-migrations-bundle": "^3.2", // 3.4.x-dev
"doctrine/orm": "^2.16", // 2.18.x-dev
"knplabs/knp-time-bundle": "dev-main", // dev-main
"pagerfanta/doctrine-orm-adapter": "4.x-dev", // 4.x-dev
"pagerfanta/twig": "4.x-dev", // 4.x-dev
"symfony/asset": "6.4.*", // 6.4.x-dev
"symfony/asset-mapper": "6.4.*", // 6.4.x-dev
"symfony/console": "6.4.x-dev", // 6.4.x-dev
"symfony/dotenv": "6.4.x-dev", // 6.4.x-dev
"symfony/flex": "^2", // 2.x-dev
"symfony/form": "6.4.x-dev", // 6.4.x-dev
"symfony/framework-bundle": "6.4.x-dev", // 6.4.x-dev
"symfony/monolog-bundle": "^3.0", // dev-master
"symfony/runtime": "6.4.x-dev", // 6.4.x-dev
"symfony/security-csrf": "6.4.x-dev", // 6.4.x-dev
"symfony/stimulus-bundle": "2.x-dev", // 2.x-dev
"symfony/twig-bundle": "6.4.x-dev", // 6.4.x-dev
"symfony/ux-autocomplete": "2.x-dev", // 2.x-dev
"symfony/ux-live-component": "2.x-dev", // 2.x-dev
"symfony/ux-turbo": "2.x-dev", // 2.x-dev
"symfony/ux-twig-component": "2.x-dev", // 2.x-dev
"symfony/validator": "6.4.x-dev", // 6.4.x-dev
"symfony/web-link": "6.4.*", // 6.4.x-dev
"symfony/yaml": "6.4.x-dev", // 6.4.x-dev
"symfonycasts/dynamic-forms": "dev-main", // dev-main
"symfonycasts/tailwind-bundle": "dev-main", // dev-main
"tales-from-a-dev/flowbite-bundle": "dev-main", // dev-main
"twig/extra-bundle": "^2.12|^3.0", // 3.x-dev
"twig/twig": "^2.12|^3.0" // 3.x-dev
},
"require-dev": {
"doctrine/doctrine-fixtures-bundle": "^3.4", // 3.6.x-dev
"phpunit/phpunit": "^9.5", // 9.6.x-dev
"symfony/browser-kit": "6.4.*", // 6.4.x-dev
"symfony/css-selector": "6.4.*", // 6.4.x-dev
"symfony/debug-bundle": "6.4.x-dev", // 6.4.x-dev
"symfony/maker-bundle": "^1.51", // dev-main
"symfony/panther": "^2.1", // v2.1.1
"symfony/phpunit-bridge": "7.1.x-dev", // 7.1.x-dev
"symfony/stopwatch": "6.4.x-dev", // 6.4.x-dev
"symfony/web-profiler-bundle": "6.4.x-dev", // 6.4.x-dev
"zenstruck/browser": "1.x-dev", // 1.x-dev
"zenstruck/foundry": "^1.36" // 1.x-dev
}
}
What JavaScript libraries does this tutorial use?
// importmap.php
return [
'app' => [
'path' => './assets/app.js',
'entrypoint' => true,
],
'js-confetti' => [
'version' => '0.11.0',
],
'@hotwired/stimulus' => [
'version' => '3.2.2',
],
'@symfony/stimulus-bundle' => [
'path' => './vendor/symfony/stimulus-bundle/assets/dist/loader.js',
],
'tom-select' => [
'version' => '2.3.1',
],
'tom-select/dist/css/tom-select.default.css' => [
'version' => '2.3.1',
'type' => 'css',
],
'@hotwired/turbo' => [
'version' => '7.3.0',
],
'stimulus-popover' => [
'version' => '6.2.0',
],
'debounce' => [
'version' => '1.2.1',
],
'turbo-view-transitions' => [
'version' => '0.3.0',
],
'stimulus-use' => [
'version' => '0.52.1',
],
'flowbite' => [
'version' => '2.2.1',
],
'@popperjs/core' => [
'version' => '2.11.8',
],
'flowbite-datepicker' => [
'version' => '1.2.6',
],
];
32 Comments
As an alternative of hardcoding (Tailwind) classes into the Stimulus controller, you can also use Stimulus CSS Classes.
They use a logical name approach, but can be a bit tricky to understand and write.
For example:
Or alternatively use the Twig helper:
And add to
modal_controller.js:And use the class:
I admit this might not the best use-case to leverage this, because you probably want the overflow-hidden behaviour everytime you're using the
modal_controller.But still wanted to share the solution. Especially when using multiple classes this can help out.
Cool! I was not aware of that feature. Thank you for sharing it!
If anyone has issues getting the modal to close by adding
data-turbo-temporary, check you came to the page via a Turbo navigation. If you've landed on the page without a Turbo transition then going back & forward again won't remove the dialog.This took me a while to work out. I hope I save someone the same.
I think that this might have happened because I initially navigated to the page before I commented out the
if (shouldPerformTransition()) Turbo.cache.exemptPageFromCache();inapp.js.Yo @ToG!
That might make sense. When I read your first comment, I wasn't sure I understood: the element should be removed not matter how you got to the pae. So this explanation, indeed, makes more sense to me :).
Cheers!
Regarding the "blur on backdrop" topic... got that working with CSS:
Check out the navigation menu: https://www.bikesport-schindler.de/
Hey @barbieswimcrew ,
That's cool! Thanks for sharing it with others :)
Cheers!
I use Encore to build the project. And I added the line in app.js.
After running 'npm run build' in the console, I get the following error:
does anyone use Encore? And can someone help me with the problem?
Hey @Bart-V
It is telling you the package
turbo-view-transitionswas not found (for some reason). Can you double-check that it's installed?You can install it by running
php bin/console importmap:require turbo-view-transitionsCheers!
Great tutorial, thanks !
Related to the Turbo Page Cache issue discuss at the end of the video,
data-action="turbo:before-cache@window->modal#close"was not working for me. After reading the documentation (https://turbo.hotwired.dev/reference/events#document) I replacedwindowbydocumentand it's working, sodata-action="turbo:before-cache@document->modal#close". I don't know if it's due to a recent update or something else, just wanted to give the information.I have an other issue however. When I am on the page containing the button and the modal, then I go to another page, then I decide to go back to the previous page using the back button of my browser, nothing happen when I click on the button to open the modal. I must refresh the page to be able to open the modal via the button. In other words, navigate to the page containing the modal via the back navigation deactivate somehow the button. Does anyone know how to solve this ?
Hey @BigBenJr
Thank you for the observation. I'm not sure why "window" is not working for you - I've used it before and it works
About the modal, when you go back, does the
openattribute remains on thedialogelement? I'm guessing theclose()method is missing something to make this work on all scenariosCheers!
Thanks for your reply @MolloKhan !
I just realized that because I added
data-turbo-temporaryto the dialog element, when I navigate to the page containing the modal via the back navigation, because the page I see is from the cache, the dialog element is not anymore on the page. If I don't adddata-turbo-temporaryto the dialog I don't have this bug anymore. Anyway, as I useturbo:before-cache@document->modal#closeI don't needdata-turbo-temporaryanymore.EDIT: I just realized as well that the 2 problems were related. Since I removed
data-turbo-temporarynow I may usewindowinstead ofdocument.Ha! I love it when a single change fixes the whole thing. Cheers!
I'm having inconsistencies in the fadein animation "'fade-in': 'fadeIn .5s ease-out;'," between local and production.
I'm hosting a containerized symfony app on Azure.
The problem is the animation duration. It works fine on my local environment, and the fade in animation is correctly defined on the dialogue element as "fadeIn 0.5s ease-out" in the chrome dev tools.
But when the container is pushed to production, I still see the animate-fade-in class on the dialogue but the definition changed to just "fadeIn ease-out", with the duration missing and so no animation actually occurs.
Hey Nick,
Hm, probably something is missing. It's impossible to know for sure, so I may just give you some tips on where to look.
Make sure it's not a cache problem on production, try to open the website in a different browser, in incognito mode, and try hard reloading in your browser. If you're not using hashes for your CSS files, browsers can cache assets that lead to this behavior. But still, even if you're using hashes for assets, try to use hard reload, incognito mode, and a different browser. Such services like Azure may have some layers of caching for assets, probably it's a good idea to make sure that cache is not the reason, I bet there should be some mechanisms to refresh it.
Also, probably the Symfony prod mode causes this problem, try to clear all the cache and run your project locally in Symfony
prodmode. Do you still have that 0.5s animation? Probably you include some extra CSS files only in prod Symfony mode and that overrides the default behaviour that you have in dev mode.And finally, double-check that nothing in your CSS files can override the default behavior, probably you have the same declaration in a few files that overrides each other, the files' load order may sometimes be different on a different OS causing a weird behavior, like on some it works and on some it does not.
I would also suggest you to double-check your build process locally and on production, probably you have different versions of some tools that are installed, and that version difference may cause different behavior.
I hope that helps!
Cheers!
Hi Ryan,
I'm using the dialog on page that contains a form, and it turns out that when the modal closes, it submit the form. I had to add an event.preventDefault() in the close method.
It seems the <form method="dialog"> conflicts with the main on. I removed it and tied the close button to a close() method in the controller. I'll follow the next course and see what happen when posting the form in the modal... but i fear this will mix up badly.
Hey Denis,
Are you trying to follow this course on your personal project? We do recommend you download the course code and follow this course from the start/ directory to have the exact code the course author has in the videos. Yes, you can follow it on a personal project, or a fresher (latest) Symfony version but it may cause more work from your side. In your case, I suppose you need to improve the business logic that's responsible for sending the form, probably using a more specific CSS selector to send the form that is only in the dialog in case it's trying to send every form on the page, etc.
I hope this helps!
Cheers!
Feeling a bit ashamed for asking, since I haven't paid yet for any course. In future, while hopefully beeing wealthier, I will.
For the form to work within a dialog, the method needs to be set as 'dialog'
Normally I'm using twig helpers for creating forms. But that doesn't seem to work in this case, am I write?
Doing so, according to the docs and as expected, when using something different than 'get' and 'post' as method, 'post' is used and the specified method gets put in a hidden form field like this:
Do I just have to write the starting of the form by hand in that case?
Hey Nexo,
If you have a Symfony form type where you want to add a hidden field - I would recommend you to add that field right in the form type, see the
HiddenTypefor that specific case: https://symfony.com/doc/current/reference/forms/types/hidden.htmlThen you will be able to render your form with Twig helper, e.g.
{{ form(form) }}.But in case you want/need to put that field manually in the form - then you can render your form with
{{ form_start(form) }}and{{ form_end(form) }}methods, between which you will place your custominput. But putting extra fields into your form manually may lead to an error in Symfony forms, so better to do it in the form type and put manually only buttons - those are safe to be added to the form.Cheers!
Hi Victor,
Thank you for the answer. I might have been not clear enough. I was talking about when setting the form type in the controller other than get/put, this hidden field will be generated by Symony. In my case it was 'dialog', because that's what I thought is necessary for using forms in <dialog>. I couldnt make the modal dialog getting automatically closed while using a form created in a controller. So I thought, I need to write the form start by hand, instead of using {{form_star(form) }}, for having 'dialog' as form method instead of a hidden field.
While using method 'dialog' would clearly close the dialog, the form didnt got submitted. Back using 'put' as form method, the form got submitted, but the dialog kept beeing open. Eventually I found my problem.
I messed up the empty <turbo-frame id="modal"> while extending twig templates.
Greetings!
Hey Nexo,
Oh, I see now... Glad you were able to find the problem youself, sometimes it's very simple problem but difficult to be spotted, and you need to double-check everything. Thanks for sharing the evential solution with others btw! It definitely might be useful or someone
Cheers!
What if we would need to fire this dialog by clicking on different elements on the page, e.g. by buttons in header and footer so we couldn't surround and put it into same button container/controller?
Should we avoid then using Stimulus, or make some application controller with global, custom events firing or what would be the pattern?
Thank you!
Hi @Kirill!
Three possibilities on this:
A) If you load what's in the modal via Ajax, you can use our AJAX-loading system that we build in the next few days to load from anywhere.
B) In many cases, if you have multiple button that open the same modal (and it is not an Ajax modal), then it's often simple enough to embed that modal in all 3 places. I don't see a huge problem with that, though I'm sure you will have some cases where you have a big modal and really want to avoid this.
C) You can always create some small system to do what you're saying. I have created a
modal-open-controller.jsbefore that I put in the<button>that should open a modal. I pass in a value calledidto the controller. The controller then finds a the modal on the page with thatidand opens it (it opens it by firing and event on the modal - e.g.modal:open- which I list for from mymodal-controller.js... though even easier might just be to put theidon the<dialog>itself, then calldocument.getElementById(this.idValue).showModal().The point is: we have a few options and they're all pretty great and are fairly simple.
Cheers!
Hi! Thank you!
In
model_controller:clickOutside()I expected you to callthis.close()instead ofthis.dialogTarget.close(). I understand that callingdialogTarget.close()triggersmodel_controller.close()but couldthis.close()have been used instead and had the same result?Yo @kbond!
Yup, definitely. The way we've written things (which I really like), we can call
this.close()directly... orthis.dialogTarget.close()(which will then trigger ourthis.close()). Probablythis.close()would be even a bit simpler, I admit :).Cheers!
I searched for this question in the comments, I had the same reasoning ! Thanks @kbond !
I have mixed feelings about that. In close() method, we called the dialog element close() method.
But, with the same syntax we call the close() method of our controller. Did I understand good ?
Hey @Adn-B!
Let me see if I understand what you're asking. Let's look at the 2 situations of what we could do inside of
clickOutside():this.dialogTarget.close(). In this case, the<dialog>will, of course, close. That will trigger the browser to dispatch thecloseevent on the<dialog>. Because of ourdata-actionon the<dialog>, that will call theclose()method on our controller. This time, because the<dialog>is already closed, it will not close again. But we CAN run our cleanup code.this.close(). This case should have the same result, but it's a bit simpler: ourclose()method closes the dialog and cleans things up. Closing the dialog should also trigger theclosemethod from the browser... and that should causeclose()to be called again. But because the dialog is already closed, we can't close it again. I think our "cleanup clode" inclose()may be called twice, but it's not a big deal.Let me know if that helps... or if I just answered the completely wrong question ;).
Cheers!
I'm seeing an error with the adding of the
turbo:before-cache@window->modal#closeaction.This is what I see in the console…
Anyone else had this & worked out why?
OK. So I've updated the
close()method as follows as it appears that Firefox Developer Edition may remove the dialog before calling theclose()handler…I wonder if this is something to do with the View Transitions support in FF?
Same problem here, thanks @ToG
@julien_bonnier was it also on FF for you? If so, normal version of dev edition?
I'll add a note about this either way - just curious to learn more.
Thanks!
"Houston: no signs of life"
Start the conversation!