gstreamer0.10-ffmpeg
gstreamer0.10-plugins-good
packages.
On the article show page, if you check the console... it's an error!
$ is undefined
Coming from article_show.js
. This shouldn't be surprising. And not just because I seem to make a lot of mistakes. Open that template and go to the bottom. Ah, this brings in a js/article_show.js
file:
... lines 1 - 80 | |
{% block javascripts %} | |
{{ parent() }} | |
<script src="{{ asset('js/article_show.js') }}"></script> | |
{% endblock %} |
Go find that: in public/
, I'll close build/
and... there it is:
$(document).ready(function() { | |
$('.js-like-article').on('click', function(e) { | |
e.preventDefault(); | |
var $link = $(e.currentTarget); | |
$link.toggleClass('fa-heart-o').toggleClass('fa-heart'); | |
$.ajax({ | |
method: 'POST', | |
url: $link.attr('href') | |
}).done(function(data) { | |
$('.js-like-article-count').html(data.hearts); | |
}) | |
}); | |
}); |
This contains some traditional JavaScript from a previous tutorial. The problem is that the global $
variable doesn't exist anymore. If you look closely on this page, you'll see that, at the bottom, we include the app.js
file first and then article_show.js
. And, of course, the app.js
file does import jQuery:
... lines 1 - 10 | |
import $ from 'jquery'; | |
... lines 12 - 26 |
But as we learned, this does not create a global variable and local variables in Webpack don't "leak" beyond the file they're defined in.
So... this file is broken. And that's fine because I want to refactor it anyways to go through Encore so that we can properly import the variable on top.
Before we do that, let's organize one tiny thing. In assets/js
, create a new components/
directory. Move get_nice_messages.js
into that... and because that breaks our build... update the import statement in app.js
to point here:
... lines 1 - 14 | |
import getNiceMessage from './components/get_nice_message'; | |
... lines 16 - 26 |
Ok: I originally put this code into a separate file because it's only needed on the article show page. We could copy all of this, put it into app.js
... and that would work! But sometimes, instead of having one big JavaScript file, you might want to split page-specific CSS and JavaScript into their own files.
To do that, we'll create a second Webpack "entry". Move article_show.js
into assets/js/
. Next, go into webpack.config.js
and, up here, call addEntry()
again. Name it article_show
and point it at ./assets/js/article_show.js
:
... lines 1 - 2 | |
Encore | |
... lines 4 - 19 | |
.addEntry('app', './assets/js/app.js') | |
.addEntry('article_show', './assets/js/article_show.js') | |
... lines 22 - 72 | |
; | |
... lines 74 - 75 |
Now when we build Webpack, it will still load app.js
, follow all the imports, and create app.js
and app.css
files. But now it will also load article_show.js
, follow all of its imports and output new article_show.js
and article_show.css
files.
Each "entry", or "entry point" is like a standalone application that contains everything it needs.
And now that we have this new article_show
entry, inside show.html.twig
, instead of our manual <script>
tag, use {{ encore_entry_script_tags('article_show') }}
:
... lines 1 - 80 | |
{% block javascripts %} | |
{{ parent() }} | |
{{ encore_entry_script_tags('article_show') }} | |
{% endblock %} |
I don't have a link
tag anywhere... nope - it's not hiding on top either. That's ok, because, so far, article_show.js
isn't importing any CSS. And so, Webpack is smart enough to not output an empty article_show.css
file. But you could still plan ahead if you wanted: encore_entry_link_tags()
will print nothing if there's no CSS file. So, no harm.
Ok: because we made a change to our webpack.config.js
file, stop and restart Encore:
yarn watch
And... cool! The app
entry caused these three files to be created... thanks to the split chunks stuff, and article_show
just made article_show.js
.
If you find your browser and refresh now... oh, same error... because we still haven't imported that. Back in article_show.js
, import $ from 'jquery'
:
import $ from 'jquery'; | |
$(document).ready(function() { | |
... lines 4 - 16 | |
}); |
Refresh again and... boom! Error is gone. We can click the fancy JavaScript-powered heart icon.
Because we haven't imported any CSS yet from article_show.js
, we already saw that Webpack was smart enough to not output a CSS file. But! Open up _articles.scss
. Part of this file is CSS for the article show page... which doesn't really need to be included on every page:
... lines 1 - 68 | |
/* ARTICLE SHOW PAGE */ | |
.show-article-container { | |
width: 100%; | |
background-color: #fff; | |
} | |
.show-article-container.show-article-container-border-green { | |
border-top: 3px solid green; | |
border-radius: 3px; | |
} | |
.show-article-img { | |
width: 250px; | |
height: auto; | |
border-radius: 5px; | |
} | |
.show-article-title { | |
font-size: 2em; | |
} | |
.like-article, .like-article:hover { | |
color: red; | |
text-decoration: none; | |
} | |
@media (max-width: 991px) { | |
.show-article-title { | |
font-size: 1.5em; | |
} | |
.show-article-title-container { | |
max-width: 220px; | |
} | |
} | |
.article-text { | |
margin-top: 20px; | |
} | |
.share-icons i { | |
font-size: 1.5em; | |
} | |
.comment-container { | |
max-width: 600px; | |
} | |
.comment-img { | |
width: 50px; | |
height: auto; | |
border: 1px solid darkgray; | |
} | |
.commenter-name { | |
font-weight: bold; | |
} | |
.comment-form { | |
min-width: 500px; | |
} | |
@media (max-width: 767px) { | |
.comment-form { | |
min-width: 260px; | |
} | |
.comment-container { | |
max-width: 280px; | |
} | |
} |
Let's copy all of this code, remove it, and, at the root of the css/
directory, create a new file called article_show.scss
and... paste!
/* ARTICLE SHOW PAGE */ | |
.show-article-container { | |
width: 100%; | |
background-color: #fff; | |
} | |
.show-article-container.show-article-container-border-green { | |
border-top: 3px solid green; | |
border-radius: 3px; | |
} | |
.show-article-img { | |
width: 250px; | |
height: auto; | |
border-radius: 5px; | |
} | |
.show-article-title { | |
font-size: 2em; | |
} | |
.like-article, .like-article:hover { | |
color: red; | |
text-decoration: none; | |
} | |
@media (max-width: 991px) { | |
.show-article-title { | |
font-size: 1.5em; | |
} | |
.show-article-title-container { | |
max-width: 220px; | |
} | |
} | |
.article-text { | |
margin-top: 20px; | |
} | |
.share-icons i { | |
font-size: 1.5em; | |
} | |
.comment-container { | |
max-width: 600px; | |
} | |
.comment-img { | |
width: 50px; | |
height: auto; | |
border: 1px solid darkgray; | |
} | |
.commenter-name { | |
font-weight: bold; | |
} | |
.comment-form { | |
min-width: 500px; | |
} | |
@media (max-width: 767px) { | |
.comment-form { | |
min-width: 260px; | |
} | |
.comment-container { | |
max-width: 280px; | |
} | |
} |
Both app.js
and article_show.js
are meant to import everything that's needed for the layout and for the article show page. app.scss
and article_show.scss
are kinda the same thing: they should import all the CSS that's needed for each spot.
At the top of article_show.scss
, we don't strictly need to do this, but let's @import 'helper/variables
to drive home the point that this is a standalone file that imports anything it needs:
@import './helper/variables'; | |
/* ARTICLE SHOW PAGE */ | |
... lines 4 - 74 |
Finally, back in article_show.js
add import '../css/article_show.scss'
:
import '../css/article_show.scss'; | |
import $ from 'jquery'; | |
... lines 3 - 19 |
Ok, check your terminal! Suddenly, gasp! Webpack is outputting an article_show.css
file! And wow! You can really see code splitting in action! That vendors~app~article_show.js
probably contains jQuery, because Webpack saw that it's used by both entries and so isolated it into its own file so it could be re-used.
Anyways, back in show.html.twig
copy the javascripts
block, paste, rename it to stylesheets
and then change to encore_entry_link_tags()
:
... lines 1 - 80 | |
{% block javascripts %} | |
{{ parent() }} | |
{{ encore_entry_script_tags('article_show') }} | |
{% endblock %} | |
{% block stylesheets %} | |
{{ parent() }} | |
{{ encore_entry_link_tags('article_show') }} | |
{% endblock %} |
That should do it! Move over, refresh and... cool! The page still looks good and the heart still works. If you inspect element on this page, in the head
, we have two CSS files: app.css
to power the layout and article_show.css
to power this page.
At the bottom, we have 4 JavaScript files to power the two entrypoints. By the way, WebpackEncoreBundle is smart enough to not duplicate the vendors~app~article_show.js
script tag just because both entries need it. Smart!
Next: we are close to having our whole app in Encore. Let's refactor a bunch more un-Webpack-ified code.
Hi Marouane,
I'm not sure what framework are you using for animations (if I'm correct to assume that you want some animations automatically applied to certain elements in your DOM), but for this, I recommend you use ReacJS animation features specifically. While we don't have a tutorial that targets these features specifically, you can check our ReactJS tutorial at https://symfonycasts.com/screencast/reactjs, or refer to ReactJS documentation on how to apply this! https://reactjs.org/docs/animation.html.
Alternatively, though much harder to achieve without bugs, you can listen for changes in the DOM within your code and fire animations on specific events: https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver. Hope this helps!
Thank you for your quick response.
I'm not using a framework, its a static template called fuse with a js file triggering ripple effect animation through css classes and using jquery.
I've imported that file in my app.js and i want to re-load it after rendering a react js component. Is that possible ?
Hmm, I suppose you could instead of loading the script on the template, load it once your react components have finished mounting. Using the proper hook, you could do something like this:
script = document.createElement('script');
script.src ='your script url here';
script.type = 'text/javascript';
script.async = true;
target = document.getElementsByTagName('script')[0];
target.parentNode.insertBefore(script, target);
Then you can manage exactly when your script is loaded and executed.
Hello Sf cast,
Is it bad for performance to split the code into multiples files ? Isn't it better to import new JS into app.js ?
Cheers!
Hey Virgile,
Did you finish this course? You need to understand how Webpack Encore works. app.js is a special file that contains scripts that should be included on *every* page. But entries are specific, they contain JS code only for specific pages. Well, probably including 1 file instead of 2 is better, but I don't think it's a big difference on practice between 1 and 2 included files, another thing when we're talking about including 10 files :) And even with 1 or 2 files it still depends. A lot of people are working on optimizing Webpack work, and on practice including one gigantic unique file not always better than 2 but smaller. Anyway, Webpack is optimized by many people every day, and you get that optimization for free when using it.
Cheers!
Hello
I feel like I'm missing some migrations. I kept watching the videos until this point anyway but I don't have articles nor space ice cream on the left side. Maybe this comes from other tutorials but I don't want to watch those. Is it possible to update the course code with all the content ?
Cheers!
Hey Julien,
I just double-checked the course for you and it works well, I don't have any missed migrations. Here's the way I tested it:
1. Download and extract the course code
2. Go to start/ directory
3. Install Composer deps with: "composer install"
4. Make sure I don't have the DB: "bin/console doctrine:database:drop --force"
5. Create the DB: "bin/console doctrine:database:create"
6. Run migrations: "bin/console doctrine:migration:migrate"
7. Check that schema is valid: "bin/console doctrine:schema:validate"
Actually, the same for finish/ directory. Please, make sure you execute these steps and if you still have invalid DB schema - you can run "bin/console doctrine:schema:update --dump-sql" to see what's exactly wrong, probably you just have a different MySQL version and it requires some specific queries to be executed, so you can just execute them with "bin/console doctrine:schema:update --force" and that's it.
I hope this helps!
Cheers!
Hi team,
Is there a way to use Encore but still using the good old JS in the javascript Twig tag ?
Example in a Twig template :
`
{% block javascripts %}
{{ parent() }}
<script>
$( document ).ready(function() {
$('#htmlElement').html('Come on Ryan ! Are you really gonna make me create a specific .js file for this ?')
$('#mytable').bootstrapTable({ })
});
</script>
{% endblock %}
`
Hey Kaizoku
And the answer is YES! You just need to set them global inside your encore endpoint. You can find more information about it in this chapter https://symfonycasts.com/sc... or search official encore documentation.
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
}
}
Hello,
Thank you for this awesome tutorial.
I'm currently working with a template that has a specific min.js file responsible for activating some js stuff like ripple effect etc. I've imported that js file in my app.js which i included in my base layout, everything is good now for my static html. The problem is with my reactjs app, when i render it i lost that ripple effect (especially with my async components) because app.js is called once in the first rendering. Is there a way to fix this please.