gstreamer0.10-ffmpeg
gstreamer0.10-plugins-good
packages.
The inline code in base.html.twig
isn't working anymore because we've eliminated the $
global variable:
<html lang="en"> | |
... lines 3 - 17 | |
<body> | |
... lines 19 - 90 | |
{% block javascripts %} | |
... lines 92 - 95 | |
<script> | |
$('.dropdown-toggle').dropdown(); | |
$('.custom-file-input').on('change', function(event) { | |
var inputFile = event.currentTarget; | |
$(inputFile).parent() | |
.find('.custom-file-label') | |
.html(inputFile.files[0].name); | |
}); | |
</script> | |
{% endblock %} | |
</body> | |
</html> |
Woo! To make it work, let's move all this code into app.js
:
... lines 1 - 15 | |
console.log(getNiceMessage(5)); | |
$('.dropdown-toggle').dropdown(); | |
$('.custom-file-input').on('change', function(event) { | |
var inputFile = event.currentTarget; | |
$(inputFile).parent() | |
.find('.custom-file-label') | |
.html(inputFile.files[0].name); | |
}); |
Instead of global variables, we're importing $
and that's why it's called $
down here:
... lines 1 - 10 | |
import $ from 'jquery'; | |
... lines 12 - 17 | |
$('.dropdown-toggle').dropdown(); | |
$('.custom-file-input').on('change', function(event) { | |
var inputFile = event.currentTarget; | |
$(inputFile).parent() | |
.find('.custom-file-label') | |
.html(inputFile.files[0].name); | |
}); |
It's all just local variables.
Try it now. Ok, it sorta works. It logs... then explodes. The error has some Webpack stuff on it, but it ultimately says:
dropdown is not a function
Click the app.js
link. Ah, it's having trouble with the dropdown()
function. That is one of the functions that Bootstrap adds to jQuery. And... it makes sense why it's missing: we're running all of our code here, and then including Bootstrap:
<html lang="en"> | |
... lines 3 - 17 | |
<body> | |
... lines 19 - 90 | |
{% block javascripts %} | |
{{ encore_entry_script_tags('app') }} | |
... lines 93 - 94 | |
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/js/bootstrap.min.js" integrity="sha384-alpBpkh1PFOepccYVYDB4do5UnbKysX5WZXm3XxPqe5iKTfUKjNkCk9SaVuEZflJ" crossorigin="anonymous"></script> | |
{% endblock %} | |
</body> | |
</html> |
It's simply not adding the function in time! Well actually, it's a bit more than that. Even if we moved this script tag up, it still wouldn't work. Why? Because when you include Bootstrap via a script tag, it expects jQuery to be a global variable... and that - wonderfully - doesn't exist anymore.
Let's do this properly.
Oh, by the way, this popper.js
thing is here because it's needed by Bootstrap:
<html lang="en"> | |
... lines 3 - 17 | |
<body> | |
... lines 19 - 90 | |
{% block javascripts %} | |
{{ encore_entry_script_tags('app') }} | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.3/umd/popper.min.js" integrity="sha384-vFJXuSJphROIrBnz7yo7oB41mKfc8JzQZiCq4NCceLEaO4IHwicKwpJf9c9IpFgh" crossorigin="anonymous"></script> | |
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/js/bootstrap.min.js" integrity="sha384-alpBpkh1PFOepccYVYDB4do5UnbKysX5WZXm3XxPqe5iKTfUKjNkCk9SaVuEZflJ" crossorigin="anonymous"></script> | |
{% endblock %} | |
</body> | |
</html> |
You'll see how this works in Webpack in a moment. Delete both of the script tags:
<html lang="en"> | |
... lines 3 - 17 | |
<body> | |
... lines 19 - 90 | |
{% block javascripts %} | |
{{ encore_entry_script_tags('app') }} | |
{% endblock %} | |
</body> | |
</html> |
Then, find your terminal and run:
yarn add "bootstrap@^4" --dev
Oh, and how did I know that the package name was bootstrap
? Just because I cheated and searched for it before recording. Go to https://yarnpkg.com/ and search for "Bootstrap". 9.7 million downloads... in the last 30 days... that's probably the right one.
And... it's done! Oh, and there's a little notice:
bootstrap has an unmet peer dependency popper.js
We'll come back to that in a minute.
Back in app.js
installing Bootstrap isn't enough. On top, add import 'bootstrap'
:
... lines 1 - 10 | |
import $ from 'jquery'; | |
import 'bootstrap'; // adds functions to jQuery | |
... lines 13 - 26 |
Nope, we don't need to say import $ from
or anything like that. Bootstrap is a jQuery plugin and jQuery plugins are... super weird. They do not return a value. Instead, they modify jQuery and add functions to it. I'll add a note here because... it just looks strange: it's weird that adding this allows me to use the tooltip()
function, for example.
But wait a second. If Bootstrap modifies jQuery... internally, how does it get the jQuery object in order to do that? I mean, jQuery is no longer global: if we need it, we need to import it. Well... because Bootstrap is a well-written library, it does the exact same thing. It detects that it's in a Webpack environment and, instead of expecting there to be a global jQuery
variable, it imports jquery
, just like we are.
And, fun fact, when two different files import the same module, they get back the same, one instance of it - a lot like Symfony's container. We import jQuery and assign it to $
. Then, a microsecond later, Bootstrap imports that same object and modifies it:
... lines 1 - 10 | |
import $ from 'jquery'; | |
import 'bootstrap'; // adds functions to jQuery | |
... lines 13 - 26 |
By the time we get past line 12, the $
variable has the new tooltip()
function.
But... you may have noticed that, while I was talking about how awesome this is all going to work... my build was failing!
This dependency was not found:
popper.js
inbootstrap.js
This is awesome! Bootstrap has two dependencies: jQuery but also another library called popper.js
. Internally, it tries to import both of them. But, because this is not installed in our project, it fails. By the way, if you're wondering:
Why doesn't Bootstrap just list this as a dependency in its
package.json
so that it's automatically downloaded for us?
Excellent question! And that's exactly how we would do it in the PHP world. Short answer: Node dependencies are complicated, and so sometimes it will work like this, but sometimes it's a better idea for a library to force us to install its dependency manually. That's called a "peer" dependency.
Anyways, this is a great error, and it even suggests how to fix it: npm install --save popper.js
. Because we're using Yarn, we'll do our version of that command. Back in your open terminal tab, run:
yarn add popper.js --dev
When that finishes... ah. Because we haven't modified any files, Webpack doesn't know it should re-build. Let's go over here and just add a space. That triggers a rebuild which is... successful!
Try it out - refresh! No errors.
Next! I have a surprise! Webpack has already started to silently optimize our build through a process called code splitting. Let's see what that means and learn how it works.
Hey Tim K.!
Ah yes, there's some confusing naming happening! So, a few things:
1) That './bootstrap' import is referring to a new "file that bootstraps some functionality" that Symfony provides in newer projects. So, it has nothing to do with "Bootstrap" the framework (that's the confusing naming part).
2) When you import 'bootstrap'
, that imports the Bootstrap JavaScript, not the Bootstrap CSS. To get the CSS, you'll want to do this same thing from inside a CSS file - https://symfonycasts.com/screencast/webpack-encore/css#referencing-em-just-em-the-package-name
Let me know if that helps! But the Bootstrap 5 JavaScript does, I believe, work a bit differently than in Bootstrap 4. So if you import it like you are doing here, it will not automatically the functions to jQuery (e.g. $('.some-element').dropdown()) like we show in this tutorial. That doesn't change anything about how Encore works - but I wanted you to be aware. That's the reason we explicitly mention to install version 4 (you can TOTALLY install version 5 if you want - but there may be a few differences).
Cheers!
thanks! It's working well now.
In case you or other might be interessted: As I try to avoid JQuery in my fresh project, I adapted the code a little bit to enable e.g. the usage of ToolTips
<b>app.js</b>
`
// any CSS you import will output into a single css file (app.css in this case)
import './styles/app.css';
// start the Stimulus application
import './bootstrap'; // Remark: unfortunately called 'bootstrap', but it is not BOOTSTRAP (getbootstrap.com)
// load BOOTSTRAP's JavaScript (install with 'yarn add bootstrap --dev')
import * as bootstrap from 'bootstrap'; // Remark: "import 'bootstrap'" will not work e.g for ToolTips
// activate BOOTSTRAP-Tooltip everywhere
// see: https://getbootstrap.com/docs/5.1/components/tooltips/#example-enable-tooltips-everywhere
const tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'))
const tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
return new bootstrap.Tooltip(tooltipTriggerEl)
})
`
Hey Ryan,
Please how are the things about actual situation with using bootstrap and webpack encore? I have simillar problems with JavaScript as Tim K. and I am not sure if my solution is good and acceptable. I am using Symfony 6.1.7 with webpack encore. I have installed bootstrap
version 5.2.2
(via yarn add bootstrap --dev
) and @popperjs/core
version 2.11.6
.
I have followed the official documentation page Using Bootstrap CSS & JS
There is this advice to include bootstrap JavaScript files this way:
app.js
...
const $ = require('jquery');
// this "modifies" the jquery module: adding behavior to it
// the bootstrap module doesn't export/return anything
require('bootstrap');
...
I found a information on Bootstrap website - version 5 does not require jQuery library. So I have added only require('bootstrap');
I try to use Tooltips from Bootstrap, so my app.js
file looks like this:
/*
* Welcome to your app's main JavaScript file!
*
* We recommend including the built version of this JavaScript file
* (and its CSS file) in your base layout (base.html.twig).
*/
// any CSS you import will output into a single css file (app.css in this case)
import './styles/app.scss';
// start the Stimulus application
import './bootstrap';
// Loads Bootstrap JavaScript
require('bootstrap');
// Enable Tooltips
// see: https://getbootstrap.com/docs/5.2/components/tooltips/
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]');
const tooltipList = [...tooltipTriggerList].map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl));
And part of Twig template like this
<button type="button" class="btn btn-secondary"
data-bs-toggle="tooltip" data-bs-placement="top"
data-bs-custom-class="custom-tooltip"
data-bs-title="This top tooltip is themed via CSS variables.">
Custom tooltip
</button>
But Tooltips are not working and I got a warning from my webbrowser Uncaught ReferenceError: bootstrap is not defined
So I found a solution in this discussion
My app.js
file
...
// Loads Bootstrap JavaScript
import * as bootstrap from 'bootstrap';
...
So I have three questions
1) What I do wrong in steps according to official documentation (Using Bootstrap CSS & JS)
2) What diferrence is between require('bootstrap')
and import * as bootstrap from 'bootstrap';
3) Is using import * as bootstrap from 'bootstrap';
correct?
Thank you!
Regards Tomas
Howdy Tomas!
I feel like every time I use Bootstrap in a project, I always run into issue's getting it to work correctly. But, I created a "template" so to speak that I use in just about all of my projects now when I reach for Bootstrap.
// package.json
{
"devDependencies": {
"@popperjs/core": "^2.11.5",
"bootstrap": "^5.1.3",
...
// app.js
// any CSS you import will output into a single css file (app.css in this case)
import './scss/global.scss'; // I use SASS so this for the most part instead of vanilla CSS
// start the Stimulus application
import './bootstrap';
import { Popover } from 'bootstrap'; // eslint-disable-line no-unused-vars
// global.scss
// Configuration
@import "bootstrap/scss/functions";
@import "variable-overrides"; // This is just another scss file I overwrite bootstrap vars in
@import "bootstrap/scss/variables";
@import "map-overrides"; // Same for any Bootstrap Map overrides
@import "bootstrap/scss/maps";
@import "bootstrap/scss/mixins";
@import "bootstrap/scss/utilities";
// Layout & components
@import "bootstrap/scss/root";
@import "bootstrap/scss/reboot";
@import "bootstrap/scss/type";
//@import "bootstrap/scss/images";
@import "bootstrap/scss/containers";
@import "bootstrap/scss/grid";
@import "bootstrap/scss/tables";
@import "bootstrap/scss/popover";
.......
// Helpers
@import "bootstrap/scss/helpers";
// Utilities
@import "bootstrap/scss/utilities/api";
I hope that helps, to answer your questions directly:
2) What diferrence is between require('bootstrap') and import * as bootstrap from 'bootstrap';
require('.....')
was how we use to include additional JS into a file before ES6 which introduced the concept of "modules". With ES6, we can use import()
other JS modules. They are sort of like using standard PHP functions vs Object Oriented PHP if that makes sense.
3) Is using import * as bootstrap from 'bootstrap'; correct?
You can do that, but I believe that will import all of the JS that is bundled with Bootstrap - which will result in larger file sizes for the end users. Personally, I prefer to only import exactly what I need from third-party libraries vs importing everything that library may provide.
Hi,
I'm trying to do the same things with bootstrap@5.1.0 and jquery@3.6.0.
For open a modal window ($('#confirmationModal').modal('show');) . I get error : .modal is not a function
I see in Bootstrap docs :
Bootstrap 5 is designed to be used without jQuery, but it’s still possible to use our components with jQuery. If Bootstrap detects jQuery in the window object it’ll add all of our components in jQuery’s plugin system;
Is it possible to help Bootstrap to find jQuery?
Thank you.
Hey Ruslan!
I haven't tried this yet, but I just checked the code and it looks like it's a requirement that jQuery be defined on the window
variable at the time that bootstrap is still imported. So, you'll need something like this before you import bootstrap:
const $ = import('jquery');
global.jQuery = $;
// global. should work - but you can try window. if it doesn't for some reason
import { Modal } from 'bootstrap';
Just be careful not to start relying on the global jQuery variable (i.e. import it whenever you need it). If you want to play it safe, you could probably unset the jQuery variable after importing from bootstrap - e.g. "delete global.jQuery".
Cheers!
Hey Ruslan!
Excellent question! Because if you looked at the bootstrap.js source code, you would find code that looks like this:
// the core bootstrap.js module
const Popper = import('popper.js');
// ...
Popper.createPopper(...);
This is a simplified version of what you would actually find there, but conceptually, it's correct. The point is: bootstrap.js itself imports and uses popper.js. All WE need to do is make sure that popper.js is downloaded into our project so that the import('popper.js')
line in the bootstrap code doesn't fail.
Let me know if that helps :).
Cheers!
Hello, im kind'a new to webpack encore. Having an issue with some bootstrap/jQuery. Seems like only half of it is working - like carousel , I don't get any errors in console, webpack builds successfully - but my dropdowns don't work - using bootstrap 4.4.1 and jQuery 3.4.1. When i use cdn of bs/jq - all is working. Would appreciate any help
Hey Andynec
Have you watched this chapter? https://symfonycasts.com/sc...
I think you hit that bug with jQuery plugins. If it's not the case this thread may help you out https://github.com/froala/K...
Cheers!
Hey. Did watch that chapter now - but most of it i have done already, and the jquery was also set globally. Funny part is - that this line helped me out in app.js require('bootstrap/dist/js/bootstrap.bundle'); although i have also import './bootstrap'; Anyways - thanks for the response!
Interesting! I'm not sure why importing that file fixed your problem. I'll grant it to jQuery weirdness. Anyway, I'm glad to know you could fix your problem.
Cheers!
Heads up, Bootstrap 5 was released on May this year and it removed jQuery. If you yarn add bootstrap --dev
, it will install that version and you will still get that dropdown is not a function
error.
To follow this tutorial along, the easiest thing to do is to use the Bootstrap version used in it, typing yarn add bootstrap@^4.3.1
in your terminal instead.
So, in my project, I imported jQuery, bootstrap, font awesome, the bought template SCSS, other dependencies and now it's taking 35 seconds to compile...
How to do to not compile dependencies for each SASS change ?
Hey Florent Hazard
Sometimes the building process may take a while, Webpack is getting smarter at compiling faster but it's still a heavy process. I'd recommend you to just run yarn dev
(without the watcher) so the assets don't get compiler on every change, but the changes won't be applied until you manually compile again
Cheers!
Hey @Karl!
That's an excellent question! We talk about it in this other tutorial - https://symfonycasts.com/sc...
The short answer is that I *also* used to do this. And there is nothing wrong with it at all. But depending on how your JavaScript is organized, it may be completely redundant :).
Cheers!
can you add a possible solution for fontawesome-svg - importing only used icons ? it will drastically improve speed of the required files for a website :)
Hey @cybernet2u!
This isn't my exact department here at SymfonyCasts (I don't deal too much with icons, CSS, etc), but I believe we handle FontAwesome by building our own custom pack on their site that only has what we need. I'm not sure if that's the only way or best way :p. If you're importing a package directly and only want to import the icons you want, then I think it would be up to you to manually import the files you need (if that's possible, given how the library is organized).
Cheers!
// composer.json
{
"require": {
"php": "^7.1.3",
"ext-iconv": "*",
"aws/aws-sdk-php": "^3.87", // 3.91.4
"composer/package-versions-deprecated": "^1.11", // 1.11.99
"knplabs/knp-markdown-bundle": "^1.7", // 1.7.1
"knplabs/knp-paginator-bundle": "^2.7", // v2.8.0
"knplabs/knp-time-bundle": "^1.8", // 1.9.0
"league/flysystem-aws-s3-v3": "^1.0", // 1.0.22
"league/flysystem-cached-adapter": "^1.0", // 1.0.9
"liip/imagine-bundle": "^2.1", // 2.1.0
"nexylan/slack-bundle": "^2.0,<2.2.0", // v2.1.0
"oneup/flysystem-bundle": "^3.0", // 3.0.3
"php-http/guzzle6-adapter": "^1.1", // v1.1.1
"sensio/framework-extra-bundle": "^5.1", // v5.3.1
"stof/doctrine-extensions-bundle": "^1.3", // v1.3.0
"symfony/asset": "^4.0", // v4.2.5
"symfony/console": "^4.0", // v4.2.5
"symfony/flex": "^1.9", // v1.17.6
"symfony/form": "^4.0", // v4.2.5
"symfony/framework-bundle": "^4.0", // v4.2.5
"symfony/orm-pack": "^1.0", // v1.0.6
"symfony/security-bundle": "^4.0", // v4.2.5
"symfony/serializer-pack": "^1.0", // v1.0.2
"symfony/twig-bundle": "^4.0", // v4.2.5
"symfony/validator": "^4.0", // v4.2.5
"symfony/web-server-bundle": "^4.0", // v4.2.5
"symfony/webpack-encore-bundle": "^1.4", // v1.5.0
"symfony/yaml": "^4.0", // v4.2.5
"twig/extensions": "^1.5" // v1.5.4
},
"require-dev": {
"doctrine/doctrine-fixtures-bundle": "^3.0", // 3.1.0
"easycorp/easy-log-handler": "^1.0.2", // v1.0.7
"fzaninotto/faker": "^1.7", // v1.8.0
"symfony/debug-bundle": "^3.3|^4.0", // v4.2.5
"symfony/dotenv": "^4.0", // v4.2.5
"symfony/maker-bundle": "^1.0", // v1.11.5
"symfony/monolog-bundle": "^3.0", // v3.3.1
"symfony/phpunit-bridge": "^3.3|^4.0", // v4.2.5
"symfony/profiler-pack": "^1.0", // v1.0.4
"symfony/var-dumper": "^3.3|^4.0" // v4.2.5
}
}
// package.json
{
"devDependencies": {
"@symfony/webpack-encore": "^0.27.0", // 0.27.0
"autocomplete.js": "^0.36.0",
"autoprefixer": "^9.5.1", // 9.5.1
"bootstrap": "^4.3.1", // 4.3.1
"core-js": "^3.0.0", // 3.0.1
"dropzone": "^5.5.1", // 5.5.1
"font-awesome": "^4.7.0", // 4.7.0
"jquery": "^3.4.0", // 3.4.0
"popper.js": "^1.15.0",
"postcss-loader": "^3.0.0", // 3.0.0
"sass": "^1.29.0", // 1.29.0
"sass-loader": "^7.0.1", // 7.3.1
"sortablejs": "^1.8.4", // 1.8.4
"webpack-notifier": "^1.6.0" // 1.7.0
}
}
Hey Ryan,
I installed Bootstrap v5.1 (which comes with the
yarn add bootstrap --dev
).But it seems not to be imported, but something called Stimulus???
I tried this in
app.js
, but no Bootstrap Styling is applied.`
// any CSS you import will output into a single css file (app.css in this case)
import './styles/app.css';
// start the Stimulus application
// import './bootstrap';
// load bootstrap (install with 'yarn add bootstrap --dev')
import 'bootstrap';
`
Any idea?