How to import jQuery only in certain modules with Webpack 4? - javascript

I'm using Webpack 4 in a Codeigniter project. A lot of my Javascript code still depends on jQuery (installed through npm), so I have this Webpack configuration:
plugins: [
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
'window.jQuery': 'jquery',
}),
It works, but I'm planning to ditch jQuery, or at least to depend less on it, so instead of importing it globally, I would like to import it only in the modules where it's needed.
I tried removing the above config and added it in a module:
import { $, jQuery } from 'jquery';
import Dropzone from 'dropzone';
When loading the page I receive this error in the console:
myDropzone.js:95 Uncaught TypeError: Object(...) is not a function
at Object.<anonymous> (myDropzone.js:95)
at r (bootstrap:76)
at t (bootstrap:43)
at bootstrap:134
at bootstrap:134
The code causing the error:
$('.dropzone').each(function () { // <--- this line
$(this).dropzone(config);
});
In the console I tried checking for $ and jQuery and the first one works fine, but with jQuery I get an error saying that it's not defined

Ok, I am pretty sure the following happens:
You are loading the jQuery dropzone plugin somewhere high up in the dependencies tree, probably loaded by some other package. The plugin expects to find the jQuery object in the global scope, in order to attach itself to it (hence giving you access to the $().dropzone method, see here ). By using the ProvidePlugin when the dropzone plugin tries to attach itself onto the jQuery object, there is no problem
However since you are now removing the ProvidePlugin the dropzone plugin never manages to attach itself and so there is no $().dropzone method available for use, so you get that error
Long story short, as long as you are depending on external code that assumes that jQuery is loaded in the global scope, you cannot truly eliminate the usage of webpack.ProvidePlugin, unless of course you directly modify the source code of those dependencies.

Try using
import $ from 'jquery';
window.jQuery = $;
window.$ = $;

Related

Can not include JQuery without npm in Vue JS project

I'm trying to import JQuery without using npm to my Vue JS project. Here's what I'm trying:
***jquery-functions.js***
import '../../public/js/jquery.min.js'
export function bar(){ $(".foo"){...} }
/$ is not defined as function/
var hello is evaluated in module scope, this prevents variables from leaking to global scope.
In order for a global to be defined, it should be assigned explicitly as such:
window.hello = function (x){x+5};
As for jQuery, it's specific to how the module works. It's UMD module and it isn't exposed as a global when jquery.min.js is imported as a module.
It should be assigned explicitly as a global either:
import jQuery from '../../public/js/jquery.min.js';
window.$ = window.jQuery = jQuery;
Or this can be done by means of Webpack that is used by Vue CLI internally, as the answer in related question suggests.
I tried everything above but somehow I couldn't get it working. Either eslint was giving function not defined error for $() or bootstrap couldn't see the included jQuery. I don't know if it's ok to include jQuery globally but I found the solution by including jQuery to my index.html file with <script> tags. Then in my jquery-functions.js file I disabled eslint then everything was working just fine on run time.
***jquery-functions.js***
/* eslint-disable */
export function bar(){ $(".foo"){...} }
I did it like that so that I can separate jQuery methods from from Vue JS methods. It might be better to use npm to avoid including jQuery globally so that you can include it only in components that you need. For me I was concerned with compatibility of all other (6 of them) jQuery dependent libraries included in the template that I downloaded from internet.

expose-loader doesn't expose modifications to exposed object

I'm using Webpack 2, Bootstrap 3, and TypeScript, and attempting to integrate npm and packaged bundles into an existing application. I'm using ProvidePlugin to make jQuery available, and expose-loader to expose jQuery to external dependencies.
(Any combination of (<any> global).$ = global.jQuery = $; or webpackmodule: { rules [{}] } configurations wouldn't work, but eventually I got the following to work:
plugins: ([
// make sure we allow any jquery usages outside of our webpack modules
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery",
jquery: "jquery",
"window.jQuery": "jquery",
"window.$": "jquery"
}),
]),
entry.ts:
import "expose-loader?$!jquery"
import "expose-loader?jQuery!jquery"
However, when I then try and call import "bootstrap" I can call $(...).popover() within my module, and I can call $(...) or jQuery(...) outside the module, but I can't call $(...).popover outside the module, instead I get this error:
Uncaught TypeError: $(...).popover is not a function
How do I make methods that are added to jQuery (like the bootstrap popover method) available in the global scope, outside of my modules?
I found my issue:
PluginProvider was exposing a different version of jQuery to the application than expose-loader was exposing. Bootstrap was initializing on the PluginProvider jQuery, but a different instance of jQuery was being exposed to the window.
So to make it work, delete PluginProvider and just use the expose-loader. And manually import jQuery where you need it as a side-effect of losing PluginProvider.

Importing an old ES5 module for use in a ReactJS component

I'm trying to use an ES5 module in a new ReactJS application and I'm struggling to understand how to correctly import that module, such that the main function within it can be found and executed.
I'm loading the module;
import 'air-datepicker';
I know I'm doing something wrong here and that it's not as simple as this, for an old library that doesn't have proper exports!
Anyway, then I should be able to manually initialise a date picker using an existing div like this;
$('#myDiv').datepicker();
I've tried multiple variations of the import and require, but I'm always getting the same error - 'datepicker is not a function'.
The library I'm experimenting with is air-datepicker. I've installed the module using npm without problems and I know the library works perfectly without React on a simple page loading the script manually in a script tag. My ReactJS app is a basic template created using 'create-react-app', from the FB tutorial pages.
If you're using create-react-app, you should be able to import it like
import 'air-datepicker/dist/css/datepicker.min.css';
import 'air-datepicker';
If you added your jQuery using <script> tag in your HTML, you need to add this line before the air-datepicker imports
const $ = window.jQuery;
const jQuery = window.jQuery;
If you added jQuery using npm install, you'll have to add these following lines
import $ from 'jquery';
import jQuery from 'jquery';
window.$ = $;
window.jQuery = jQuery;
//... air-datepicker imports
Make sure to initialize it inside your componentDidMount or somewhere you're sure that the element has been mounted already.
componentDidMount() {
$('#my-element').datepicker();
}
render() {
return <div>
<input
id="my-element"
type='text'
className="datepicker-here"
data-position="right top" />
</div>
}
Well, that's a day of my life that I'm never getting back!
The problem was caused by Babel import ordering. Import statements are hoisted - meaning they are evaluated first, before any other code (i.e. my window. assignments) are executed. This is 'by design' in babel.
The only way to avoid Babel hoisting, from what I can tell, is to avoid 'import' altogether and use require instead.
So, in the following order the global $ will have been set when you come to require air-datepicker. If you try to 'import' air-datepicker it won't work because Babel will evaluate all of your import statements before executing the window. assignment.
import $ from 'jquery';
window.$ = $;
require('air-datepicker');
There are one or two other approaches that also would have worked, but they are all less desirable because they need you to manually configure webpack - i.e. 'ejecting' the create-react-app config and going it alone...
Use the imports-loader;
// only works if you disable no-webpack-loader-syntax
require("imports?$=jquery!air-datepicker");
or, use the ProvidePlugin, making the module available for all modules.
You have to give it an identifier:
import datepicker from 'air-datepicker';

RequireJS Passing Dependencies

Not sure if this has been asked already but I am just wondering if importing a dependency using RequireJS to one file and then importing that file to another means the second file has to import the same dependency as the first file.
example -
file A
define([
'jquery'
],function($) {
//jquery thing here
}
File B
define([
'A'
],function(A) {
// do some jquery thing here
}
or
define([
'jquery','A'
],function($,A) {
// do some jquery thing here
}
It is good modularization practice to have each module list its own dependencies explicitly rather than silently depend on a module being loaded through an intermediary. So your module B should take the second form you suggested for it: define(['jquery', 'A'], function ($, A) {....
Now, strictly speaking, you could have B only require A and you would be able to use jQuery in B without B having an explicitly dependency on jQuery. This is doable because jQuery, even when it is loaded as an AMD module, by default leaks $ and jQuery into the global space just like it does when it is loaded outside RequireJS. However, it is not good practice. You should not rely on this though unless you have a substantial reason to do so and document it very verbosely.
You will have to use the third option, because otherwise $ will be undefined in the module B. But this doesn't mean RequireJS will import jQuery twice internally, it will just pass you the $ variable.
define([
'jquery','A'
],function($,A) {
// $ was passed as a function argument.
}

RequireJS - change jQuery module name

I have a file called "lib.jquery.js" (all libraries are starting with "lib." and not reusable application modules are starting with "app.") in my baseUrl directory, and a module definition:
define([
"lib.jquery"
],
function(
jQuery
){
console.log(jQuery) // undefined
})
But jQuery here is undefined because jQuery module name is hardcoded inside it as "jquery" but not "lib.jquery". How do I configurate RequireJS correctly to make all loading modules check "lib.jquery" file when requesting "jquery" or force jQuery module to be named "lib.jquery"?
The jquery script defines the module as 'jquery' and expects that you will simply reference it as 'jquery'. This is done because one should not load 2 jquery files of different versions.
Workaround would be put the lib.*.js files into a separate directory called lib.
define(['lib/jquery'], function (jQuery) {
});
I have found the solution here: Use requirejs and jquery, without clobbering global jquery?
Renamed file lib.jquery.js to lib.jquery-core.js
Created file lib.jquery.js with "jquery loader module":
define(['lib.jquery-core'], function () {
return jQuery.noConflict(true);
});
Now it's working as intended to. Hardcoding names is evil.

Categories

Resources