I am trying to achieve the following:
bundle (in this order) jquery, tether, and bootstrap.js.
load this bundle within a HTMLpage and beneath it load other page specific scripts.
To achieve this I am using webpack 2 and the CommonsChunkPlugin. Here is my config.
For entries I have:
const scriptsEntry = {
blog_index: SRC_DIR + "/js/pages/blog_index.js",
blog_about: SRC_DIR + "/js/pages/blog_about.js",
vendor: ["jquery", "tether", "bootstrap"]
};
In the plugins section:
scriptsPlugins.push(
new webpack.optimize.CommonsChunkPlugin({
name: "vendor",
filename:"vendor.bundle.js",
minChunks: 2
}),
...
}));
Inside 'index.html' I load the bundles:
<script src="{{ url_for('static', filename='dist/js/vendor.bundle.js') + anti_cache_token }}"></script>
<script src="{{ url_for('static', filename='dist/js/home.js') + anti_cache_token }}"></script>
Inside the source directory in the blog_index.js I make use of jquery:
import $ from "jquery";
$(".home-click").click(function () {
alert("clicked from .home-click");
});
The result:
everything bundles without any errors.
when I click .home-click the alert box fires as expected.
checking the bundled files I see that:
vendor.bundle.js contains: jquery, tether, and bootstrap.
looking inside, for instance, blog_index.js (after it was run through webpack 2), I notice that the jquery import is not bundled inside this file, but vendor.bundle.js (this is as expected).
However, I have the following problem when I check the browser console:
I tried switching the order of the libraries in this line vendor: ["jquery", "tether", "bootstrap"], but nothing changed--the error is still there.
Do you know how can I solve this, preferably without using additional webpack plugins?
Bootstrap's javascript assumes that jQuery is hooked on the window object, it does not require it or anything.
By bundling stuff up, you do not expose variables to the global scope, or at least you should not be doing that. So the bootstrap code cannot find jQuery.
Add this to your plugins and you should be ok
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
'window.jQuery': 'jquery',
tether: 'tether',
Tether: 'tether',
'window.Tether': 'tether',
})
This will replace all instances where jQuery is assumed as global with the export of the jQuery package, which is the jQuery object. Same for Tether
Related
I'm using Laravel-mix and Webpack to combine and control scripts for a site.
I'm my main app.js I have these lines:
var jQuery = require( 'jquery' );
require( './vendor/polyfill-library.min.js' );
require( './vendor/polyfill-init.js' ); // which is using jQuery
In polyfill-init.js I'm doing this: jQuery( document ).ready( .... That results in this error:
Uncaught ReferenceError: jQuery is not defined
I also get this error if I try and mix it together using Laravel-mix, by adding this to my webpack.mix.js:
mix.js( [
'resources/js/app.js',
'resources/js/vendor/polyfill-library.min.js',
'resources/js/vendor/polyfill-init.js',
], 'assets/js/app.js')
I assume the error is because Webpack keeps the required/imported scripts in seperate namespaces/environments, so no conflicts occur. And that's all fine and dandy, - but I don't know how to combine two required/imported scripts, so they're using the same namespace/environment.
... If I copy all the code into app.js (instead of requiring it), then it works, but it's not pretty. Not pretty at all.
I looked at Webpack's documentation to see if there's a way to define a dependency for an imported/a required script, but either it's not there; or I'm not getting it.
I also looked at the 7 billion ways that this post suggest that I'd do it, - but I'm trying to figure out how Webpack/Laravel-mix want me to do it.
So my question is... Are there a way that I can do something like this:
var jQuery = require( 'jquery' );
require( './vendor/polyfill-library.min.js' );
require( './vendor/polyfill-init.js' ); // which is using jQuery
... and let Webpack know, that polyfill-init can use jQuery?
You can use provideplugin:
plugins: [
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery"
})
]
Try window.jQuery = require( 'jquery' );
polyfill-init.js probably looks for jQuery in global scope while the var jQuery is only available in the local scope of this module.
U should externalize JQuery from the webpack.
index.html
<script
src="https://code.jquery.com/jquery-3.1.0.js"
integrity="sha256-slogkvB1K3VOkzAI8QITxV3VzpOnkeNVsKvtkYLMjfk="
crossorigin="anonymous">
</script>
webpack.config.js
module.exports = {
//...
externals: {
jquery: 'jQuery'
}
};
polyfill-init.js
import $ from 'jquery';
Refer more details here https://webpack.js.org/configuration/externals/
I installed bootstrap 4, jquery, popper by npm. When I load website it show error in console about can't get file popper.js, I check again that I see one file popper.js was load by require.js and one file popper.js was noticed was not found by bootstrap. They have different path. file success have path: http://localhost:9000/Libs/popper.js/dist/umd/popper.js and file fail is http://localhost:9000/popper.js
In below picture, pic 1 show configure path of library js. Pic 2 show librarys was load success, and pic 3 notice popper.js was not load.
I don't understand why bootstrap can't recognize popper.js was loaded by requie.js.
Everyone can help me explain about problem for me.
Thanks!
Try using:
bootstrap.bundle.js
or:
bootstrap.bundle.min.js
https://getbootstrap.com/docs/4.0/getting-started/contents/
The docs show that those two files mentioned come with popper included so you won't need to require it.
If that doesn't work search your source files for /popper.js it may be as simple as fixing a reference somewhere else.
Instead of doing this:
require(["bootstrap"], function(bootstrap) {
// do nothing
});
Try this which loads popper first:
require(["popper"], function(popper) {
window.Popper = pop
require(["bootstrap"]);
});
Alternatively you can use the CDN links. Bootstrap suggests using the CDN versions before you include the compiled bootstrap.
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
Hey this is my first response on a stackoverflow question but since I have been struggling with it myself, I wanted to share my answer. Just like other answers say, bootstrap requires the popper js but it references popper itself on the root so it doesn't matter if you load it or not, bootstrap looks for it on the root while you have it on a deeper directory. Look at the code below and you can see it uses require('popper.js'):
bootstrap.js
The solution is to use require to define module "popper.js" and exactly as this name like below:
require.config({
baseUrl: 'lib',
paths: {
main: 'main',
jquery: 'jquery/dist/jquery',
'jquery-slim': 'jquery/dist/jquery-3.3.1.slim.min',
'jqueryUnobtrusive': 'jquery-validation-unobtrusive/jquery.validate.unobtrusive',
'bootstrap': 'bootstrap/dist/js/bootstrap',
'knockout': 'knockout/knockout-latest',
'ajax': 'jquery-ajax/jquery.unobtrusive-ajax',
'shop': '/scripts/shop/shop',
'domready': 'requirejs/domready',
'shoporder': '/scripts/shoporder/shoporder',
'popper': 'popper.js/dist/umd/popper'
}});
define("popper.js", ['popper'], () => {
require(['popper'], () => {
require(['bootstrap']);
});
define(['main', 'jquery', 'jquery-slim'], () => {
require(['jqueryUnobtrusive', 'ajax']);
require(['knockout']);
require(['jquery']);
require(['popper.js']);
});
This way bootstrap looks for the correct directory.
Bootstrap requires Popper to be available in the global scope, you can solve the issue by adding exports to the shim configuration:
shim: {
bootstrap: {
deps: ['jquery','popper']
},
popper: {
exports: "Popper"
}
}
Also you need to make sure that bootstrap defines the name of the module correctly. You can check that in the opening lines the bootstrap.js file
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined'
? factory(exports, require('jquery'), require('popper'))
: typeof define === 'function' && define.amd
? define(['exports', 'jquery', **'popper'**], factory)
: (...)
Maybe I have fundamentally misunderstood how requirejs config works but I thought my configuration below made some libraries global so I could just use them in other files while only having to require and define files that I needed to use within the individual script. However I cannot reference $ (jQuery) in my application code without getting a reference error indicating it is not globally accessible. I've isolated the problem to the simple example below.
My file set up is as follows:
test
|
|-index.html
|-TestApp.js
|-MainApp.js
|-lib
| |-require.js
| |-jquery.js
| |-loadash.js
| |-backbone.js
|-css
|-test.css
The library file versions are RequireJS 2.1.22, jQuery 2.0.3, Loadash 3.10.1 and Backbone 1.2.1. I'm just trying to set up my environment and the approach I am taking is to pass my TestApp.js file to require.js to load the required files and bootstrap the application code in MainApp.js. The script in index.html is as follows:
<!DOCTYPE html>
<html>
<head>
<link rel='stylesheet' type='text/css' href='css/test.css'/>
</head>
<body>
<div></div>
<script src="./lib/require.js" type="text/javascript" data-main="./TestApp.js"></script>
</body>
</html>
The referenced css script file simply ensured the div is visible as an orange square. See below:
div {
height: 100px;
width: 100px;
background-color: #FA6900;
border-radius: 5px;
}
It's the script line in index.html that then kicks off the application code by passing my configuration file to requirejs. This is the TestApp.js passed across as data-main. The TestApp.js is here:
require.config({
paths: {
'jquery': 'lib/jquery',
'lodash': 'lib/lodash',
'backbone': 'lib/backbone'
},
map: {
'*': {
// Backbone requires underscore. This forces requireJS to load lodash instead:
'underscore': 'lodash'
}
},
shim: {
jquery: {exports: '$'},
underscore: {
deps: ['jquery'],
exports: '_'
},
backbone: {
deps: ['underscore'],
exports: 'Backbone'
},
TestApp: {
deps: ['backbone'],
exports: 'TestApp'
}
}
});
require(['MainApp'], function(MainApp) {
MainApp.run();
});
The file above references the paths to the library files I want to use, I then remap loadash to be loaded when underscore is required (I need some of the extra loadash capability), I then use the shim to ensure the dependancies are correct as the files are loaded. Passing this config file to require.js in the index.html seems to be working as all of the files are showing as loaded in my browser. However the problem seems to be they do not appear to be globally accessible as I thought they would be.
Following the config section the last require call loads the MainApp.js file and calls the exposed run function. The MainApp.js looks like this:
define(function(require) {
var run = function() {
$(document).ready(function() {
$('div').click(function() {
$('div').fadeOut('slow');
});
});
};
return {
run: run
};
});
As far as I understood I should not need to require the files I already mentioned in the require config, I thought they should be loaded and available to this code. This is where I have misunderstood what is going on or have missed a step out. The exposed run function is being called but the first line that calls $ throws the error:
ReferenceError: Can't find variable: $
So my questions are:
What have I got wrong in my thinking?
(or) What am I doing incorrectly?
What should I be doing in order to preload and make available
frequently referenced libraries so that I do not need to require and
define them in every file I have?
As far as I understood I should not need to require the files I already mentioned in the require config, I thought they should be loaded and available to this code.
You misunderstood how RequireJS works. You should read the documentation from start to finish. For now, here are things you should change.
You should require jquery in your MainApp module:
define(function(require) {
var $ = require("jquery");
You should remove your shims that you have for jquery, underscore and backbone as they all call define and shim is only for code that does not call define. I don't know what TestApp is but if it is your own code, you really should make it into a proper AMD module and remove the shim.
#Louis has made me realise the error in what I was doing above. Changing the shim in TestApp.js so that is reads:
MainApp: {
deps: ['backbone'],
exports: 'MainApp'
}
Corrected the problem, now Backbone, $ and _ are all available to the rest of my application code without cluttering up each files require. i.e. I do not need to begin every file with:
define (['lib/jquery', 'lib/loadash', 'lib/backbone'], function($, _ , Backbone) {
Given in my actual app the list of common deps is quite large this means I only need to define locally used resources and can control the paths from a single location.
My program starts through this entry point in a twig template:
<script data-main="{{ asset('bundles/wwwcms/js/app') }}" src="{{ asset('bundles/wwwcms/js/require.js') }}"></script>
This outputs:
<script data-main="static.correct_require_url.net/js/app" src="static.correct_require_url/require.js"></script>
That all works fine, so require executes my app.js (config):
requirejs.config({
'baseUrl': 'js/lib',
'paths': {
'app': '../app',
'jquery': '//code.jquery.com/jquery-1.10.2.min',
'bootstrap': '//netdna.bootstrapcdn.com/bootstrap/3.0.2/js/bootstrap.min'
},
'shim': {
'bootstrap': {
deps: ['jquery']
}
}
});
// always require jquery and bootstrap onload
requirejs(['bootstrap']);
Which also works.
The directory structure is as follows:
app.js
require.js
/lib
- test.js
/app
- dirty.js
- import.js
Okay so here is where the error comes
For a page, let's say I want to load dirty.js and import.js. dirty.js depends on jquery, and import.js depends on "test.js" (something I just made up for demonstration purposes).
So I load the dependencies on the page this way:
<script type="text/javascript">
require(['app/dirty', 'app/import']);
</script>
When the page loads, dirty.js and import.js are loaded in just fine, from the correct url relative to "app". However, the dependency for import.js (test.js) does not get loaded correctly.
This is how import.js starts:
require(['jquery', 'test'], function($, test) {
...
});
As you can see, it's asking for lib/test.js. It pulls jQuery from a CDN, so that's taken care of. But for test.js, it uses the wrong path.
import.js (and all other modules loaded first) are pulled in using the correct static url path, but dependencies for modules are loaded using the wrong path- they are loaded using the url of that page!
So when I look at the network tab in the debugger console, import.js comes from:
static.blahblah.com/js/app/import.js.
but test.js comes from:
blahblah.com/route/to/current/page/js/app/test.js
(which obviously 404s every time)
What are possible reasons for this occurring? Am I doing something wrong? Any help would be greatly appreciated.
This is how the script tags look before require runs:
<script data-main="//static.bananajams.cloud.net/bundles/wwwcms/js/app" src="//static.bananajams.cloud.net/bundles/wwwcms/js/require.js"></script>
<script type="text/javascript">
require(['app/dirty', 'app/preview', 'app/import']);
</script>
Basically data-main is only a good idea if it's the single entry point for your code. It's loaded asynchronously, so there is no guarantees it runs before the other code in your app. See
RequireJS does not run data-main script before loading required modules
for more info on this.
In my project I want to use RequireJS and bootstrap my app as follows:
requirejs.config({
baseUrl: 'scripts/vendor',
paths: {
jquery: [
'https://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min',
'jquery'
],
angular: [
'http://ajax.googleapis.com/ajax/libs/angularjs/1.0.4/angular.min',
'angular'
],
app: '../app'
}
});
require(['require', 'jquery', 'angular', 'app'], function(require, $, angular, app) {
console.log(require);
console.log($);
console.log(angular);
console.log(app);
});
On my index.html only RequireJS is loaded via script tag, where the RequireJS loads the above code.
What works:
- in my Network monitor I can see that RequireJS, jQuery, Angular and app are loaded
- The console.log messages print correct for require, jQuery and app
The angular object is somehow undefined. But if I don't load it from CDN and use my local load, it works! The local file is a RequireJS wrapper that looks like this:
define(['/components/angular/angular.min.js'], function () {
return angular;
});
How do I get this work with Angular'S CDN? Or does this depend on support from Angular?
First, you are confusing "paths" with "shim"
Path is good, don't go for "shim" behavior. But, you need to make your "paths" proper:
paths: {
jquery: 'https://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min',
// NOTE: angular is "plain JS" file
angular: 'http://ajax.googleapis.com/ajax/libs/angularjs/1.0.4/angular.min',
app: '../app'
}
Then, you need to let go of the need to have something returned to you... Just "use the force, Luke" :) and expect the right globals to be there when you need them:
require(['jquery', 'app', 'angular'], function($, app, thisValueDoesNotMatter) {
// you don't need to wrap "require" Just use global
console.log(require);
console.log($);
console.log(app);
// note, angular is loaded as "plain JavaScript" - not an AMD module.
// it's ok. It returns "undefined" but we just don't care about its return value
// just use global version of angular, which will be loaded by this time.
// because you mentioned it in your dependencies list.
console.log(window.angular);
});