yarn: $ is not defined - javascript

For the first time I am using Yarn. I have installed the latest version of Laravel Boilerplate (http://laravel-boilerplate.com/) and there is used Yarn.
My need is to include the JS library DataTables (https://datatables.net/).
Unfortunately I am new to Yarn and I am not sure if I am making everything right, because I get the error:
[Show/hide message details.] ReferenceError: $ is not defined
which is on the this line:
$(document).ready(function() {
...
This is telling me that it cannot find the jquery library, but it should be there.
Here is the webpack.mix.js code:
const mix = require('laravel-mix');
/*
|--------------------------------------------------------------------------
| Mix Asset Management
|--------------------------------------------------------------------------
|
| Mix provides a clean, fluent API for defining some Webpack build steps
| for your Laravel application. By default, we are compiling the Sass
| file for the application as well as bundling up all the JS files.
|
*/
mix.setPublicPath('public');
mix.sass('resources/sass/frontend/app.scss', 'css/frontend/frontend.css')
.sass('resources/sass/backend/app.scss', 'css/backend/backend.css')
.js('resources/js/frontend/app.js', 'js/frontend/frontend.js')
.js([
'resources/js/backend/before.js',
'resources/js/backend/app.js',
'resources/js/backend/after.js'
], 'js/backend/backend.js')
.extract([
'jquery',
'datatables.net-dt',
'bootstrap',
'popper.js/dist/umd/popper',
'axios',
'sweetalert2',
'lodash',
'#fortawesome/fontawesome-svg-core',
'#fortawesome/free-brands-svg-icons',
'#fortawesome/free-regular-svg-icons',
'#fortawesome/free-solid-svg-icons'
]);
if (mix.inProduction() || process.env.npm_lifecycle_event !== 'hot') {
mix.version();
}
Every time I call the command "yarn prod" in order to create the CSS and js files, but the DataTables are not working.
Did I miss something?
Thanks in advance!

It's not because of yarn. Yarn is a package manager, it doesn't run any part of your application's code so cannot generate an error like yours. Yarn is just for downloading packages and manage their dependencies.
Then comes Laravel Mix for you, which is just a wrapper around Webpack. Webpack reads your application code, handles your require and import commands in your .js files and then generates your bundles.
How to make it work:
I suppose you did run the yarn command (without params) in your project root once when you installed Laravel Boilerplate. There should be a lot of packages inside your node_modules directory (more than 900).
Then you did run yarn add -D datatables.net-dt also. Now you should have a datatables.net and a datatables.net-dt folder inside node_modules.
I see you've added datatables.net-dt in your webpack.mix.js, this is OK! You don't need any other require( 'datatables.net-dt' )( window, $ ); as said in the documentation. That one line in your webpack.mix.js is enough! DataTable will be inside your vendor.js.
Now create an example table with attribute id="example" in your index.blade.php then add this code to the bottom of your resources\js\frontend\app.js:
$(document).ready(function() {
$('#example').DataTable();
});
Then run yarn dev to let Webpack generate your bundles (compiled js files) and view your site in the browser. Following these, it should be working on a fresh install install of Laravel Boilerplate, without any error. I've just tested id now, works like charm.
Your possible bug:
$ is not defined tells that some part of your code is trying to use jQuery before it has been loaded.
It's important that you must write your codes using jQuery (shortened $) inside your resources\js\frontend\app.js or in a separate .js which is later required/imported into this file! It's because jQuery and other vendor packages like DataTable are stored in vendor.js, which must be loaded before any calls to them.
So don't use custom <script> tags in your html's <head> tag for your app code because that will be loaded and executed before any other defined in the bottom of your <body> tag!
Have a look at this file resources\views\frontend\layouts\app.blade.php. In the bottom of the body tag you'll see this:
<!-- Scripts -->
#stack('before-scripts')
{!! script(mix('js/manifest.js')) !!}
{!! script(mix('js/vendor.js')) !!}
{!! script(mix('js/frontend.js')) !!}
#stack('after-scripts')
Your resources\js\frontend\app.js and all its imported scripts will be compiled to this js/frontend.js file.
How jQuery is imported in Laravel Boilerplate:
This is done well by default, you don't have to bother with it. Open your resources\js\bootstrap.js and see these two lines:
import $ from 'jquery';
window.$ = window.jQuery = $;
This file will then imported by frontend/app.js. So write your code here and you'll be fine...
PS.: If this doesn't helps you to make it work, you should edit your question and provide more info on your sources. A screenshot for example taken from your Chrome DevTools, showing the lines of your JavaScript where the error occurred.

Related

Including bundled webpack file in webpack build

I have a webpack bundled widget that I pack into a single file using webpack, and can use as follows:
<script type="text/javascript" src="my-bundle.js"></script>
<script type="text/javascript">
(function() {
MyBundle.render();
}) ();
</script>
This works fine, but I want to use this widget in my main Rails app. So I've copied the my-bundle.js file into my main project directory and required it.
When I run webpack on my main app, I can see that the code in my-bundle is being included in the resulting js file, but I cannot access the code. i.e. calling MyBundle gives a not defined error.
How can I access it?
EDIT - it looks like I can just use script-loader to run the my-bundle.js file once (which defines a MyBundle function). This doesn't feel like the best way to do it though

How do you use bundles in your code when you browserify an npm module?

I want to use a javascript library called fuse.js. (https://fusejs.io/) in my vanilla javascript app.
I npm install fuse and then add
const Fuse = require('fuse.js')
...other javascript functions here
to the top of my script.js file. (i have trie removing the .js but the lib is called fuse.js in the node_modules folder)
I then run:
browserify script.js -o bundle.js
then I add the bundle.js to my index.html
<script type="module" src="bundle.js"></script>
then, no matter what I do I can't access the Fuse object or any of my other javascript functions...It does load the bundle...but it's completely "closed", I can't use any functions in it.
I have seen that the guys export the bundle to the window object, but there are a lot of people saying this isn't best practice?
Are there any good resources anyone can recommend that I can study up to understand the concept of
I see a useful library on npm, and I use it in my front-end code
(fuse.js does have a cdn that I can just include in the script tag, but I want to know for future use how to use npm modules in my front-end workflow?)

Webpack + Symfony encore - how to add libraries

I am quite new in using webback and encore, so I don't know exactly which is the approach to handle this.
I have a website with a few js files, and it uses jQuery and Bootstrap. I want to create a unique minified js file using webpack.
I've been able to successfully do it following the examples at Symfony website, so I am able to just include one app.js and encore builds the dependencies correctly.
There are some javascript libraries that are not used in my app.js but will be used in some javascript code inlined in the website, like for example the PhotoSwipe.
Currently, I am requiring the PhotoSwipe library inside my main.js file, although this file doesn't use this library.
// Resources/public/js/main.js
import PhotoSwipe from 'photoswipe';
import PhotoSwipeUI_Default from 'photoswipe/dist/photoswipe-ui-default';
So, how can I tell encore to add this library into the builded app.js without having to add it in my main.js?
I've tied to add it in webpack.config.json
.addEntry('app', [
'./src/AppBundle/Resources/public/js/main.js',
'photoswipe',
'photoswipe/dist/photoswipe-ui-default'
])
If I check the app.js generated I can see the code has been added to app.js, but when I try in my html code to call Photoswipe I get the error Uncaught ReferenceError: PhotoSwipe is not defined
I guess that this is as the libraries are confined inside the js file, and I should add some kind of export to be accessible, I don't know, as I've said, I am very new in this ;)
This is how I managed my libraries:
// Resources/public/js/myCustomScript.js
const PhotoSwipe = require('photoswipe.js)';
const PhotoSwipeUI_Default = require ('photoswipe/dist/photoswipe-ui-default');
[...] // do PhotoSwipe stuff
And in your webpack.config.json only:
.addEntry('myCustomScript', './src/AppBundle/Resources/public/js/myCustomScript.js')
Then in the templates that needs this script :
<script type="text/javascript" src="{{ asset('build/myCustomScript.js') }}"></script>

Using laravel mix to include js dependencies

i can't really get my js libraries to work , some time ago i decided to have a separate js file for every library i use (so i have a jquery.js file and a bootstrap.js file included in my layout) ,everything was working just fine until i had to add jquery-ui to this chaos , and got this error
Uncaught TypeError: $(...).slider is not a function
until i loaded ,jquery and jquery-ui in the same file .The problem is i dont want to include jquery ui everywhere i include jquery , beacuse i only use it in 2 pages. Below i will put my code :
jquery-ui.slider.js:
require('jquery-ui/ui/widgets/slider');
require('./components/carFilter.js');
app.js:
window.$ = window.jQuery = require('jquery');
require('./bootstrap');
webpack.mix.js:
mix.js('resources/assets/js/app.js', 'public/js')
mix.js('resources/assets/js/jquery-ui.slider.js', 'public/js');
I am using the following npm modules :
bootstrap-sass
jquery
jquery-ui
I ended up by just creating a file where i require jquery,bootstrap.js and then i require this file in a specific file for the two pages...
Below its the code:
app.js
window.$ = window.jQuery = require('jquery');
require('./bootstrap');
page.js
require('./app.js')
require('jquery-ui/ui/widgets/slider');
It seams that now it is working ,even if now i have to include a js file in all the views...Question its still open , i hope somone have a beter idea .
I think laravel-mix only serves the purpose when you have small sites with pages that all include the same app.js & app.css files.
When you have complex portal with multiple pages that have different set of frontend "plugins" you have to split your entries & generate a table of dependencies automatically.
After a lot of time searching I've come to the conclusion that the best approach would be switch to webpack-encore from Symfony.
You can read more about it's capabilities here Webpack Encore Official Documentation.
Now the question is how to embed it to Laravel? It's quite easy, I've just reverse engineered the Symfony's PHP bundle for that and here is the result:
https://github.com/ntpages/laravel-encore
Now you just have to include you're dependency in the page entry and it's all handled automatically.
I think you should load jquery-ui when a condition is met, like:
if (window.loadJQueryUI) {
require('jquery-ui');
}
And you need to initialize loadJQueryUI for the two pages, like this:
<script type="text/javascript">
window.loadJQueryUI = true;
</script>

how to use webpack to load CDN or external vendor javascript lib in js file, not in html file

I am using react starter kit for client side programming. It uses react and webpack. No index.html or any html to edit, all js files. My question is if I want to load a vendor js lib from cloud, how to do I do that?
It would be easy to do that in a html file. <script src="https://forio.com/tools/js-libs/1.5.0/epicenter.min.js"></script>
However, in js file, it only uses npm installed packages. How can I import the above lib with no html file? I tried import and require, they only work for local files.
update 10/21/15
So far I tried two directions, neither is ideal.
#minheq yes there is a html file sort of for react start kit. It is html.js under src/components/Html. I can put cloud lib and all its dependencies there like this:
<div id="app" dangerouslySetInnerHTML={{__html: this.props.body}} />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<script src="https://forio.com/tools/js-libs/1.5.0/epicenter.min.js"></script>
<script src="/app.js"></script>
<script dangerouslySetInnerHTML={this.trackingCode()} />
</body>
Good news is it works, I don't need do anything else in js file, no import or require. However, now I have two jquery libs loaded in different ways. One in here, the other through npm and webpack. I wonder it will give me trouble later. The react-routing I use give me 'undefined variable' error if I type a none home path in browser window due to the server side loading I guess. So this solution is not very good.
Use webpack externals feature. This is documented as: link. "You can use the externals options for applications too, when you want to import an existing API into the bundle. I.e. you want to use jquery from CDN (separate tag) and still want to require("jquery") in your bundle. Just specify it as external: { externals: { jquery: "jQuery" } }."
However, the documentation I found a few places are all fussy about how to do this exactly. So far I have no idea how to use it to replace <script src="https://forio.com/tools/js-libs/1.5.0/epicenter.min.js"></script> in html.
externals is not intended to let you do this. It means "don't compile this resource into the final bundle because I will include it myself"
What you need is a script loader implementation such as script.js. I also wrote a simple app to compare different script loader implementations: link.
var $script = require("scriptjs");
$script("//ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js", function() {
$('body').html('It works!')
});
You can create a script tag in your JS as
$("body").append($("<script src="https://forio.com/tools/js-libs/1.5.0/epicenter.min.js"></script>"))
There is one html file that is definitely being used to serve to users with your js bundle attached. Probably you could attach the script tag into that html file
Use webpack's externals:
externals allows you to specify dependencies for your library that are
not resolved by webpack, but become dependencies of the output. This
means they are imported from the environment during runtime.
I have looked around for a solution and most of all proposals were based on externals, which is not valid in my case.
In this other post, I have posted my solution: https://stackoverflow.com/a/62603539/8650621
In other words, I finished using a separate JS file which is responsible for downloading the desired file into a local directory. Then WebPack scans this directory and bundles the downloaded files together with the application.

Categories

Resources