Chapters
-
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!
Welcome to the fabulous day 4! Where we're already creating JavaScript modules... a fancy term that means we're writing import statements and export statements. And we're pulling this off entirely without a build system. Time for a happy dance!
But what about third-party packages? Head over to https://npmjs.com and search for a very important package called js-confetti
. This package is all about celebrating, which... is exactly what we're doing during these 30 days! In the README, it says to use Yarn to install it. We are not going to do that. Instead, skip right down to the usage example. Copy that, head over to our app.js
... and paste that in:
Show Lines
|
// ... lines 1 - 8 |
import JSConfetti from 'js-confetti'; | |
const jsConfetti = new JSConfetti(); | |
jsConfetti.addConfetti(); | |
Show Lines
|
// ... lines 13 - 15 |
Side note: import
statements always go at the top of your file. If you don't do that - if you do something weird like this, well, you can, but your browser will move this up to the top when it executes the code anyway. So we'll avoid being troublemakers.
Missing JavaScript Module Error
Ok: is this going to work? I mean... probably not because we haven't installed anything. But let's live recklessly and try it anyway! Error! A very important error:
Failed to resolve module specifier
js-confetti
. Relative references must start with either/
,./
or../
.
So what this is saying is that your browser found an import
statement... and has no idea how to load that file. If an import statement starts with ./
or ../
, your browser knows how to handle that: it looks for a file relative to this file. Easy peasy.
But if there is no ./
or ../
, it's called a bare module. In that case, your browser looks for a match in the importmap. Right now, our importmap looks like it did before. Notably, we do not have a js-confetti
key. And that's why we get this error.
This is one of the most important errors you'll see when coding with modules. And it'll look a bit different based on which browser you're using. Firefox, for example, phrases this error differently.
But regardless of the wording, this error almost always means the same thing: you're trying to use a third party package, but it's not installed.
Installing Packages with importmap:require
How do we install it? Glad you asked! Copy the package name, spin over and run:
php bin/console importmap:require js-confetti
That's it! Spin back over and... celebration! It works! Mad refreshing!
How does that work? Karma? Well, not surprisingly, if you view the page source, we have a new entry inside our importmap
with a js-confetti
key. And it points to a file in an assets/vendor/
directory. Interesting.
When we ran that command, it really did just one thing. It updated our importmap.php
file. It added this entry right here:
Show Lines
|
// ... lines 1 - 15 |
return [ | |
Show Lines
|
// ... lines 17 - 20 |
'js-confetti' => [ | |
'version' => '0.11.0', | |
], | |
]; |
Behind the scenes, it went out and found what the latest version was and put that here. And because we have a js-confetti
item in importmap.php
, it means that we're going to have a matching js-confetti
key inside of the importmap on the page.
The assets/vendor/ Directory
Where does that file actually live? Up here in a new assets/vendor/
directory. If you dig, here is the actual file that's being loaded.
Two juicy details about this vendor/
directory. The first is: it's ignored from Git: you can see /assets/vendor/
:
Show Lines
|
// ... lines 1 - 11 |
###> symfony/asset-mapper ### | |
Show Lines
|
// ... line 13 |
/assets/vendor | |
###< symfony/asset-mapper ### |
Just like the composer vendor/
directory, this is not something that you should commit to your repository.
The second is more of a question: how do I get these files if I clone or update a project and some or all of the files are missing?
To find out, get crazy and destroy that directory. Muwahahaha. And now, to increase our reckless streak, try to refresh the page. Error! Awesome error!
The
js-confetti
vendor asset is missing: try running theimportmap:install
command.
Lovely idea! Spin over and try that:
php bin/console importmap:install
Just like composer install
, that downloads whatever you need into assets/vendor/
... and now it just works.
That's it! By day 4, we've already solved 3rd party packages! We don't even need a giant node_modules/
directory! I'm going to have to find some other way to fill my hard drive. Maybe, more Docker containers?
Ok, for tomorrow's adventure, we'll jazz up our site with some CSS. Stay tuned!
12 Comments
Hi,
I had the same problem. I resolved it by including js files via CDN and not via importmap :
import { Calendar } from 'https://cdn.skypack.dev/@fullcalendar/core@6.1.10';
David
Hey Brandon and David!
Using the url is a great workaround - it’s so cool that you always have this as an option :).
But, of course, that shouldn’t be needed. I don’t know fullcalendar, but the first thing that looks funny is your import statement. My guess it it should look like what @dbo did, except replace the CDN url with the package name: import { Calendar } from '@fullcalendar/core';
. And so also, install this with importmap:require @fullcalendar/core
Let me know if that helps! And I’m happy to offer any more explanation into what’s going on.
Cheers!
Hi!
Firstly, thanks for the answer and all your effort in this lovely course, but for me don't solve the problem. The only way to solve it is to use the CDN URL.
If I try as you describe:
import { Calendar } from '@fullcalendar/core';
import DayGridPlugin from '@fullcalendar/daygrid/index.js';
document.addEventListener('DOMContentLoaded', function() {
const calendarEl = document.getElementById('calendar')
const calendar = new Calendar(calendarEl, {
plugins: [DayGridPlugin],
initialView: 'dayGridMonth'
})
calendar.render()
})
I have this error in the browser console:
Uncaught TypeError: class constructors must be invoked with 'new'
render https://localhost/assets/vendor/@fullcalendar/core/core.index-57623a7e3dad789a4c3310e6d9285762.js:7
Preact 23
handleRenderRequest https://localhost/assets/vendor/@fullcalendar/core/core.index-57623a7e3dad789a4c3310e6d9285762.js:7
Pe https://localhost/assets/vendor/@fullcalendar/core/core.index-57623a7e3dad789a4c3310e6d9285762.js:7
handleRenderRequest https://localhost/assets/vendor/@fullcalendar/core/core.index-57623a7e3dad789a4c3310e6d9285762.js:7
drained https://localhost/assets/vendor/@fullcalendar/core/core.index-57623a7e3dad789a4c3310e6d9285762.js:7
tryDrain https://localhost/assets/vendor/@fullcalendar/core/core.index-57623a7e3dad789a4c3310e6d9285762.js:7
request https://localhost/assets/vendor/@fullcalendar/core/core.index-57623a7e3dad789a4c3310e6d9285762.js:7
render https://localhost/assets/vendor/@fullcalendar/core/core.index-57623a7e3dad789a4c3310e6d9285762.js:7
<anonymous> https://localhost/assets/app-f58212e6a0b7bdb970ebb9053d580fdd.js:18
EventListener.handleEvent* https://localhost/assets/app-f58212e6a0b7bdb970ebb9053d580fdd.js:12
core.index-57623a7e3dad789a4c3310e6d9285762.js:7:41883
render https://localhost/assets/vendor/@fullcalendar/core/core.index-57623a7e3dad789a4c3310e6d9285762.js:7
Preact 23
handleRenderRequest https://localhost/assets/vendor/@fullcalendar/core/core.index-57623a7e3dad789a4c3310e6d9285762.js:7
Pe https://localhost/assets/vendor/@fullcalendar/core/core.index-57623a7e3dad789a4c3310e6d9285762.js:7
handleRenderRequest https://localhost/assets/vendor/@fullcalendar/core/core.index-57623a7e3dad789a4c3310e6d9285762.js:7
drained https://localhost/assets/vendor/@fullcalendar/core/core.index-57623a7e3dad789a4c3310e6d9285762.js:7
tryDrain https://localhost/assets/vendor/@fullcalendar/core/core.index-57623a7e3dad789a4c3310e6d9285762.js:7
request https://localhost/assets/vendor/@fullcalendar/core/core.index-57623a7e3dad789a4c3310e6d9285762.js:7
render https://localhost/assets/vendor/@fullcalendar/core/core.index-57623a7e3dad789a4c3310e6d9285762.js:7
<anonymous> https://localhost/assets/app-f58212e6a0b7bdb970ebb9053d580fdd.js:18
(Async: EventListener.handleEvent)
<anonymous> https://localhost/assets/app-f58212e6a0b7bdb970ebb9053d580fdd.js:12
In other case, when I use it with the CDN way
import { Calendar } from 'https://cdn.skypack.dev/@fullcalendar/core@6.1.11';
import DayGridPlugin from 'https://cdn.skypack.dev/@fullcalendar/daygrid@6.1.11';
Fix the problem, but I think that this workaround don't be needed if we have the local files.
I don't understand why not works using this local files instead of CDN URL files.
I found that there are some related issues in this GitHub repos, but I'm not sure if is the same problem because is more related to ESM and jsdelivr
https://github.com/fullcalendar/fullcalendar/issues/7474
https://github.com/tattali/CalendarBundle/issues/70
Thanks!
Hi,
Have you tried to importmap:require @fullcalendar/daygrid
and use it as import dayGridPlugin from '@fullcalendar/daygrid'
?
Cheers!
Yes I try it but shows me the same error
Uncaught TypeError: class constructors must be invoked with 'new'
ok... looks not good, in reality this issue blocks this library from asset mapper usage, I tried lot of different ways of connecting it, even with bundle installing, and also using external import link and everything stucks with this error.. the problem is inside the jsDelivr it brake the package internal linking that's why it doesn't work.
Hi
I used asset mapper in my newest project and I have some confusion.
For example my template uses AOS:
I installed it with :
bin/console importmap:require aos
which resulted in a vendor/aos/aos.index.js
For AOS to work properly, I still had to include the aos.css in the header of my template:
<link href="{{ asset('styles/vendors/aos.css') }}" rel="stylesheet">
Is this the way or am I doing something wrong?
Thx for clarification. <3
It turns out for me after adding js confetti javascript and the required library installed with importmap:require, one more php bin/console asset-map:compile was required for app.js (on browser side) to be loaded at last version...
Hey @Leonard_Bira!
Weird! Hmm. Well, asset-map:compile
should only need to be run when you're deploying to production. In dev mode, if your browser makes a request to /assets/app-abcd1234.js
, that physical file doesn't exist. Instead, the request is handled by Symfony and it returns the file.
However, if you DO run asset-map:compile
on your local machine, then, definitely, if you make any changes to your source files, those won't be seen. The public/assets
directory is kind of "stuck" with the old code. If this was the case for you, you can delete public/assets/
entirely and allow Symfony to serve the files, which should then always contain the latest versions of the code :).
Cheers!
Hello, Thanks for the nice tutorials. I'm trying to get Asset-mapper implemented in my own project instead of WebPack, but for something as simple as fontawesome I'm stumped for some raison. I can import a fortawesome third-party package and I see JS and CSS included, but they themselves point to a webfont that I put in the /assets folder aswell, but they arn't visible on my Dev when running the site. getting 404's on the webfonts.
Any help or hints on how to achieve this seemingly simple task?
Thanks !
Hey @Mathias-VdB!
Let's get you unlocked so you can enjoy AssetMapper!
Ok, so I assume you ran something like importmap:require @fortawesome/fontawesome-free/css/fontawesome.min.css
and now have an entry like this in your importmap.php
file? If so, remove that, and try this instead:
php bin/console importmap:require @fortawesome/fontawesome-free/css/all.css
Let me know if that works (it should, I just tried it). Tbh, I love FontAwesome, but their install has always confused me. I don't know what this fontawesome.min.css
file is exactly: it's the CSS, but it lacks the FontFace that the all.css
has:
@font-face {
/* ... */
src: url("../webfonts/fa-regular-400.woff2") format("woff2"), url("../webfonts/fa-regular-400.ttf") format("truetype"); }
AssetMapper is pretty cool in this regard. When you require all.css
, it sees the url()
inside this file and actually downloads the font files! So with this, you'll notice (in assets/vendor/...
), that you have a webfonts
folder with these items.
Also, FontAwesome recommends you install them by including a "kit" - which is just a bit of JavaScript and then you can control what icons go in there. That's what I've been doing lately anyway.
Let me know if this helps!
Cheers!
"Houston: no signs of life"
Start the conversation!
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
}
}
Hello,
I'm trying to get FullCalendar to work on my site. I too am switching from WebPack to Asset-Mapper, but I'm struggling to see where I import Full Calendar and how with its plugins. In bootstrap.js I have:
import { Calendar } from "./vendor/@fullcalendar/core/index.js";
app.register('calendar', Calendar)
And then in my Stimulus controller I have
import { Calendar } from '@fullcalendar/core/index.js';
import dayGridPlugin from '@fullcalendar/daygrid/index.js';
const calendarEl = document.getElementById('calendar');
But I get the error:
TypeError: class constructors must be invoked with 'new'
Which I've determined is from me not getting my imports correct, because all this was working when I was using WebPack, nothing else has changed. I don't see a lot of documentation on FullCalendar and Asset-Mapper.
I've installed FullCalendar by running ./bin/console importmap:require fullcalendar
And it seems to have installed everything I need:
13 new items (fullcalendar, @fullcalendar/core/index.js, @fullcalendar/interaction/index.js,
Any help is much appreciated, thank you!