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 SubscribeOpen up the base layout: app/Resources/views/base.html.twig
. Scroll down to the bottom:
... lines 1 - 94 | |
{% block javascripts %} | |
<script src="{{ asset('bundles/fosjsrouting/js/router.js') }}"></script> | |
<script src="{{ path('fos_js_routing_js', { callback: 'fos.Router.setData' }) }}"></script> | |
<script src="{{ asset('build/layout.js') }}"></script> | |
{% endblock %} | |
... lines 100 - 103 |
In a perfect world, we would only have one JS file: layout.js
. But, we still have two other script tags that are cramping my style. One loads router.js
from FOSJsRoutingBundle, and the second populates that with the actual routes.
Thanks to these, there is a global Routing
variable, which we use in RepLogApp.js
to generate URLs: Routing.generate()
and then the route name:
... lines 1 - 9 | |
class RepLogApp { | |
... lines 11 - 120 | |
_saveRepLog(data) { | |
return new Promise((resolve, reject) => { | |
const url = Routing.generate('rep_log_new'); | |
... lines 124 - 140 | |
}); | |
} | |
... lines 143 - 189 | |
} | |
... lines 191 - 209 |
This is the last place where we are using global variables... and I want to fix that!
The curious thing about the router.js
file is that it lives in a PHP package. Instead of living in node_modules
, it lives at vendor/friendsofsymfony/jsrouting-bundle/Resources/public/js/router.js
. This is the exact file we're currently including in base.html.twig
.
Cool! So, let's just require it like normal! const Routing = require()
, then... well, a very ugly path: ../../../vendor/friendsofsymfony/jsrouting-bundle/Resources/public/js/router.js
:
... lines 1 - 6 | |
const Routing = require('../../../vendor/friendsofsymfony/jsrouting-bundle/Resources/public/js/router'); | |
... lines 8 - 210 |
Phew! Long, gross-looking... but it makes sense. Try it. Refresh!
Uh oh... it did not like that:
Type error: Cannot read property navigator of undefined
It's coming from router.js
, line 8. Huh. If you look at this file, well, it's minified... so it's pretty confusing. This file is built using something called the Google Closure compiler... which basically means that it does not play nicely with require()
. I hope this will be fixed in the library soon - there's a pull request open for it. But right now... well, there is no simple way to require()
it.
Tip
It is possible to require()
the file... but it takes some work. See
bit.ly/fosjsroutingbundle-require if you're
curious.
So... yea... we need to keep these two script tags in our base layout for now. It's not ideal, but we'll survive.
But! It does bother me that - inside RepLogApp.js
- I am relying on this global variable. I really want this file to require everything it needs. So, I use a trick!
Check this out: inside Components
, create a new file called Routing.js
. And inside that, just module.exports = window.Routing
:
... lines 1 - 4 | |
module.exports = window.Routing; |
I'll add a comment above this:
/** | |
* For now, we rely on the router.js script tag to be included | |
* in the layout. This is just a helper module to get that object. | |
*/ | |
module.exports = window.Routing; |
With this, yes, we do need to remember to put the router.js
script tag in our layout. But now, we can at least correctly use require statements everywhere else.
I mean, in RepLogApp.js
, we can say const Routing = require('./Routing')
:
... lines 1 - 6 | |
const Routing = require('./Routing'); | |
... lines 8 - 210 |
From the perspective of this file, we're not relying on any global variables. And hey! When FOSJsRoutingBundle plays nicer in the future, we can easily update things.
Refresh to make sure it works. Yes! Ok, let's move onto something completely different: requiring partial libraries... and then... replacing require()
with the much trendier import
.
"Houston: no signs of life"
Start the conversation!
// composer.json
{
"require": {
"php": "^7.2.0",
"symfony/symfony": "3.3.*", // v3.3.16
"twig/twig": "2.10.*", // v2.10.0
"doctrine/orm": "^2.5", // v2.7.0
"doctrine/doctrine-bundle": "^1.6", // 1.10.3
"doctrine/doctrine-cache-bundle": "^1.2", // 1.3.5
"symfony/swiftmailer-bundle": "^2.3", // v2.6.3
"symfony/monolog-bundle": "^2.8", // v2.12.1
"symfony/polyfill-apcu": "^1.0", // v1.4.0
"sensio/distribution-bundle": "^5.0", // v5.0.22
"sensio/framework-extra-bundle": "^3.0.2", // v3.0.26
"incenteev/composer-parameter-handler": "^2.0", // v2.1.2
"friendsofsymfony/user-bundle": "^2.0", // v2.1.2
"doctrine/doctrine-fixtures-bundle": "~2.3", // v2.4.1
"doctrine/doctrine-migrations-bundle": "^1.2", // v1.3.2
"composer/package-versions-deprecated": "^1.11", // 1.11.99
"friendsofsymfony/jsrouting-bundle": "^1.6" // 1.6.0
},
"require-dev": {
"sensio/generator-bundle": "^3.0", // v3.1.6
"symfony/phpunit-bridge": "^3.0" // v3.3.5
}
}
// package.json
{
"dependencies": [],
"devDependencies": {
"babel-core": "^6.25.0", // 6.25.0
"babel-loader": "^7.1.1", // 7.1.1
"babel-plugin-syntax-dynamic-import": "^6.18.0", // 6.18.0
"babel-preset-env": "^1.6.0", // 1.6.0
"bootstrap-sass": "^3.3.7", // 3.3.7
"clean-webpack-plugin": "^0.1.16", // 0.1.16
"copy-webpack-plugin": "^4.0.1", // 4.0.1
"core-js": "^2.4.1", // 2.4.1
"css-loader": "^0.28.4", // 0.28.4
"extract-text-webpack-plugin": "^3.0.0", // 3.0.0
"file-loader": "^0.11.2", // 0.11.2
"font-awesome": "^4.7.0", // 4.7.0
"jquery": "^3.2.1", // 3.2.1
"lodash": "^4.17.4", // 4.17.4
"node-sass": "^4.5.3", // 4.5.3
"resolve-url-loader": "^2.1.0", // 2.1.0
"sass-loader": "^6.0.6", // 6.0.6
"style-loader": "^0.18.2", // 0.18.2
"sweetalert2": "^6.6.6", // 6.6.6
"webpack": "^3.4.1", // 3.4.1
"webpack-chunk-hash": "^0.4.0", // 0.4.0
"webpack-dev-server": "^2.6.1", // 2.6.1
"webpack-manifest-plugin": "^1.2.1" // 1.2.1
}
}