Correct way of loading non-AMD compatible jQuery plugins in require.js with jQuery in noConflict mode? - requirejs

Say I want to use jquery together with a standard, non-amd enabled jquery plugin that has been defined using standard closure: (function($))( $.fn.myplugin = { ... } )(jQuery); and it all sits inside of a js/libs/jquery/jquery.myplugin.js.
I use this config:
require.config({
baseUrl: 'js/',
paths: {
'jquery': 'libs/jquery/jquery-noconflict',
'underscore': 'libs/underscore/underscore',
'backbone': 'libs/backbone/backbone',
'jquery-myplugin': 'libs/jquery/jquery.myplugin'
},
shim: {
'backbone': {
deps: ['underscore', 'jquery'],
exports: 'Backbone'
},
'jquery-myplugin': {
deps: ['jquery']
}
});
I load jQuery in no-conflict mode in libs/jquery/jquery-noconflict.js, becase I don't want to pollute global namespace:
define(['libs/jquery'], function () {
return jQuery.noConflict(true);
});
and this is how I load my main app.js:
define([
'jquery',
'underscore',
'backbone',
'jquery-myplugin'],
function($, _, Backbone, MyPlugin){
//MyPlugin is always undefined, not even sure if
//I should be passing it here if it only extends jQuery?
});
Now, here is the problem I am experiencing - while I can use all libraries defined above without any problems, I could not work out the correct shim configuration to load non-AMD enabled jquery plugins.
I've tried setting up jquery-myplugin as deps of the jquery (and other way around) but I could never get it working.
It seems like I'm having problem with the following scenario:
jQuery loads in no-conflict mode.
plugin code runs, extending the instance of the jQuery above
I can use $ within my application, extended by the plugin code, so $.myplugin is available.
I have seen similar questions floating around but none of them actually resolves this issue giving only vague suggestions such as "use shim config"...
Edit
I also tried using
"jquery-myplugin": {
deps: ["jquery"],
exports: "jQuery.fn.myplugin"
}
And whilst plugin methods are available once loaded as AMD module this way, I still can't access: $('.class').myplugin() as default $ object hasn't been extended with myplugin code.

Using jQuery.noConflict(true) removes the jQuery global variable. When your plugin loads, it tries to access jQuery, but can't, causing this failure.
If your plugin was a module, it could get access to jQuery as a dependency. Or you could leave jQuery available as a global.

First, insure that "path/to/jquery-myplugin" actually extends window.jQuery and not $
noConflict() leaves window.jQuery object defined but unbinds itself from window.$ On some new browsers window.$ is built in alias for native document.querySelectorAll function.
Second, your myplugin does NOT need to return itself, as it cannot be used by itself. Since it extends jQuery, return jQuery from myplugin call.
Lastly, "path/to/jquery-myplugin" is NOT a module. It's a plain JS file. It's possible RequireJS tries to load it like a module and does not find define() call, which leads to mess. Try actually adding ".js" file extension to the reference to signal to RequireJS that it needs to use "js!" plugin to load the resource.
require.config({
paths: {
"jquery": "path/to/jquery",
"jquery-myplugin": "path/to/jquery-myplugin.js"
},
shim: {
"jquery": {
init: function() {
return window.jQuery.noConflict();
},
"jquery-myplugin": {
deps: ['jquery']
init: function($) {
return $;
},
}
}
});

I had the same problem as you today. Here is how I could fix it :
requirejs.config({
"baseUrl": "js/lib",
"paths": {
"app": "../app"
}
});
// Final version of jQuery with all needed plugins.
define("jquery", ["jquery-core", "myplugin"], function(jqCore) {
return jqCore;
});
// Define core jQuery module.
define("jquery-core", ["jquery-1.9.1.min"], function() {
return jQuery.noConflict(true);
});
// This module exposes jquery module to the global scope and returns previous defined version.
define("jq-global", ["jquery-core"], function(jqCore) {
var tmp = jQuery;
jQuery = jqCore;
return tmp;
});
// Define your plugin here, and don't forget to reset previous jQuery version.
define("myplugin", ["jq-global", "jquery.myplugin"], function(jqGlobal, jqPlugin) {
jQuery = jqGlobal;
});
// Load the main app module to start the app
requirejs(["app/main"]);
Now in my app/main.js file I can do the following :
define(["jquery"], function($) {
$('body').myplugin();
});
The idea here is to expose jQuery temporary before plugin code is executed. So far I didn't test the solution in a larger environment with a lot more modules to load, so I can't guarantee it will work in the long term ;)
Edit
This solution won't work!! Since requirejs doesn't load the scripts sequentially, it is possible the plugin js file loads before jquery which will cause the execution to fail. Sorry for this.
If someone has another idea...

Related

How do you setup a require.js config with typescript?

Ok, I've been reading a lot of questions and answers about this, and a lot of it is rubbish.
I have a very simple question. How do I do the equivalent of this:
require.config({
paths: {
"blah": '/libs/blah/blah',
}
});
require(['blah'], function(b) {
console.log(b);
});
In typescript?
This doesn't work:
declare var require;
require.config({
paths: {
"blah": '/libs/blah/blah',
}
});
import b = require('blah');
console.log(b);
s.ts(8,1): error TS2071: Unable to resolve external module ''blah''.
s.ts(8,1): error TS2072: Module cannot be aliased to a non-module type.
error TS5037: Cannot compile external modules unless the '--module' flag is provided.
Compiling with the --module flag, with a dummy blah.ts shim compiles, but the output is:
define(["require", "exports", 'blah'], function(require, exports, b) {
require.config({
paths: {
"blah": '/libs/blah/blah'
}
});
console.log(b);
});
Looks like it might work, but actually no, the require.config is inside the require block, it is set after it is already needed.
SO! I've ended up so far with this as a solution:
class RequireJS {
private _r:any = window['require'];
public config(config:any):void {
this._r['config'](config);
}
public require(reqs:string[], callback:any):void {
this._r(reqs, callback);
}
}
var rjs = new RequireJS();
rjs.config({
paths: {
"jquery": '/libs/jquery/jquery',
"slider": '/js/blah/slider'
}
});
rjs.require(['slider'], function(slider) {
console.log(slider);
});
Which seems terrible.
So be clear, inside modules that depend on each other, this sort of thing works perfectly fine:
import $ = require('jquery');
export module blah {
...
}
I just need a proper way to setting the requirejs config at a top level, so that the imported paths for the various named modules are correct.
(...and this is because, largely, 3rd party dependencies are resolved using bower, and installed in the /lib/blah, where as the shim files I have for their definitions are in src/deps/blah.d.ts, so the default import paths are incorrect after moving the generated modules files into /js/ on the site)
NB. I've mentioned jquery here, but the problem is not that jquery doesn't work property as an AMD module; I have a shim jquery.ts.d file for this. The issue here is the requirejs paths.
Yesterday I wrote up a solution to this exact issue on my blog - http://edcourtenay.co.uk/musings-of-an-idiot/2014/11/26/typescript-requirejs-and-jquery:
TL;DR - create a config file config.ts that looks something like:
requirejs.config({
paths: {
"jquery": "Scripts/jquery-2.1.1"
}
});
require(["app"]);
and ensure your RequireJS entry point points to the new config file:
<script src="Scripts/require.js" data-main="config"></script>
You can now use the $ namespace from within your TypeScript files by simply using
import $ = require("jquery")
Hope this helps
This post is 3 years old, and there's a lot of changes that have been made when using Typescript. Anyway, after some search on the web,some research on TS documentation-these guys made some good job, I found something that could help.
so this can apply to the latest current of TS (2.2.1)
you probably know that you can use
npm install --save #types/jquery
do the same for your 3rd party JS librairies such as require
now you need to define what your TypeScript Compiler has to do, and how, so create a tsconfig.json file that contains:
// tsconfig.json file
{
"compilerOptions": {
"allowJs": true,
"baseUrl": "./Scripts",//Same as require.config
"module": "amd",
"moduleResolution": "Node",//to consider node_modules/#types folders
"noImplicitAny": false,
"target": "es5", // or whatever you want
"watch": true
}
now, let's focus on require's configuration
// require-config.ts
declare var require: any;
require.config({
baseUrl: "./Scripts/",
paths: {
"jquery": "vendor/jquery/jquery.x.y.z"
// add here all the paths your want
// personnally, I just add all the 3rd party JS librairies I used
// do not forget the shims with dependencies if needed...
}
});
so far so good
now focus on your module written in TS that would use jquery and that is located in Scripts/Module folder:
// module/blah.ts
/// <amd-module name="module/blah" />
import $ = require("jquery");
export function doSomething(){
console.log("jQuery version :", $.version);
}
So this answer looks the same as Ed Courtenay's, doesn't it?
and user210757 mentioned that it does NOT work!!!
and it does not! if you type in your console tsc -w --traceResolution, you'll see that tsc cannot find any definition for jquery.
Here's how to alleviate assuming you previously launch npm install --save #types/jquery by doing this, in a folder named node_modules\#types, you should get the TS definition for jquery
select the package.json file in jquery subfolder
look for the "main" property
set it to "jquery", the same as the alias you are using in your require.config
and done! your module would be transpiled as
define("module/blah", ["require", "exports", "jquery"], function (require, exports, $) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
function doSomething() {
console.log("jQuery version:", $.fn.jQuery);
}
exports.doSomething = doSomething;
});
and that JS code looks good to me!
I just don't like the fact that our module dependencies list "require" & "exports", that sounds like a TS issue, but anyway IT WORKS!
if you want to use import for javascript modules you need to tell typescript about it so,
declare var require;
require.config({
paths: {
"blah": '/libs/blah/blah',
}
});
// Important, place in an external.d.ts:
declare module 'blah'{
// your opportunity to give typescript more information about this js file
// for now just stick with "any"
var mainobj:any;
export = mainobj;
}
import b = require('blah');
console.log(b);
alternatively you could simply do:
var b = require('blah'); and it should work as well

Marionette JS modules, Brunch JS and require JS project structure

I am using BrunchJS to handle coffee script and assets compilation.
The project uses several Brunch plugins such as brunch-handlebar which requires the "commonjs" wrapper to operate.
Extract from my config.coffee
modules:
# We cant avoid require js wrapping since brunch modules use commonjs
# Otherwise Marionnette JS offers its own modules loading strategy
# loading mechanism
wrapper: "commonjs"
definition: "commonjs"
On the Marionette side I can have a simple application loading just fine.
index.html
<script type="text/javascript">
var app = require('application');
app.initialize()
</script>
application.coffee
# Load handlebars block helpers
require 'lib/view_helper'
class Application extends Backbone.Marionette.Application
initialize: =>
#addInitializer((options) =>
console.log "HELLO WORLD"
AppLayout = require 'views/app_layout'
#layout = new AppLayout()
#layout.render()
)
#start()
# module.exports is the object that's actually returned as the result of
# a require call.
module.exports = new Application()
Starting from there how do I use Marionette JS modules ? I read about using modules with AMD here https://github.com/marionettejs/backbone.marionette/wiki/AMD-Modules-vs-Marionette's-Modules but I can not use the define keyword in my marionette module definition since "define" and "require" are not exposed. Brunch only uses it to load its plugins and my app source files.
A usual Marionette module looks like this:
MyApp = new Backbone.Marionette.Application();
MyApp.module("Foo", function(){
// module code goes here
});
MyApp.start();
In a separate file moduleA.coffee I tried to do:
MyApp = require 'application'
define ["MyApp", "Marionette"], (MyApp, Marionette) ->
MyModule = MyApp.module("MyModule")
MyModule.addInitializer ->
console.log "HELLO FROM MODULE"
MyModule
But define is not defined.
I also tried to do:
MyApp = require 'application'
MyApp.module "ModuleA", (MyApp, ModuleA, Backbone, Marionette, $, _) ->
ModuleA.addInitializer ->
console.log "HELLO FROM MODULE"
but then I need to require all my marionette modules ("moduleA") in application.coffee and ran into some circular dependency issues.
One of the solution I am thinking about is to disable BrunchJS commonjs wrapping and load handlebars from vendor folder instead of as a brunch plugin.
JavaScript example in case it helps!
I think the important bit is initiating you bootstrapper (main.js, in this example) via require. It should be provided in a script tag attribute called 'data-main'. Perhaps this is something Brunch does for you, I'm not sure.
<script type="text/javascript" data-main="js/main" src="js/libs/require.js')}"></script>
Then your main.js might look something like this.
/* global require, console */
require.config({
baseUrl: 'js',
paths: {
'jquery': 'libs/jquery/jquery',
'underscore': 'libs/underscore/underscore',
'backbone': 'libs/backbone/backbone',
'backbone.wreqr': 'libs/backbone.wreqr/lib/backbone.wreqr',
'backbone.babysitter': 'libs/backbone.babysitter/lib/backbone.babysitter',
'marionette': 'libs/marionette/lib/core/backbone.marionette'
...
},
shim: {
'underscore': {
deps: ['jquery'],
exports: '_'
},
'backbone': {
deps: ['underscore', 'jquery'],
exports: 'Backbone'
},
'backbone.wreqr': {
deps: ['backbone', 'underscore'],
exports: 'wreqr'
},
'backbone.babysitter': {
deps: ['backbone', 'underscore'],
exports: 'babysitter'
},
'marionette': {
deps: ['backbone', 'backbone.wreqr', 'backbone.babysitter'],
exports: 'Marionette'
}
}
});
// Start the main app logic.
require([
'app'
],
function (App) {
App.start();
});
Once this is all setup, you can initiate any resource prefixing the function with define, like this...
define([
'jquery',
'underscore',
'backbone',
'marionette'
],
function ($, _, Backbone, Marionette) {
'use strict';
// code goes here
});

How to get a single javascript page using r.js

I am doing my first try using requireJS and it works great !
I now would like to use the optimizer and i meet some issues when running my code in the browser.
I have these JS files:
/public/javascripts/build.js
/public/javascripts/main.js
/public/javascripts/lib/jquery.min.js
/public/javascripts/lib/require.min.js
/public/javascripts/a.js
/public/javascripts/b.js
/public/javascripts/c.js
a.js, b.js and c.js are modules i define for my application using requireJS.
main.js:
require.config({
paths: {
'jQuery': 'lib/jquery.min.js'
},
shim: {
'jQuery': {
exports: '$'
}
}
});
require(['a.js'], function(A){
var Entity = new A();
});
build.js
({
baseUrl: ".",
paths: {
requireLib: "lib/require.min",
jquery: "lib/jquery.min"
},
name: "main",
out: "main-built.js",
include: ["requireLib"]
})
Also i am wondering why do we have to specify the paths of the libraries into the build.js and not the other javascript files.
When i do not use the optimizer and only load the file
<script src="/javascripts/lib/require.min.js" data-main="/javascripts/main"></script>
it works great, but when i run r.js -o ./public/javascripts/build.js and only load
<script src="/javascripts/main-built.js"></script> i get the error Uncaught TypeError: undefined is not a function in the minified code.
How to explain that ?
Here are the logs i get when running r.js
Tracing dependencies for: main
Uglifying file: /public/javascripts/main-built.js
/public/javascripts/main-built.js
----------------
/public/javascripts/lib/require.min.js
/public/javascripts/a.js
/public/javascripts/b.js
/public/javascripts/lib/jquery.min.js
/public/javascripts/c.js
/public/javascripts/main.js
This is definitely wrong:
require(['a.js'], function(A){
var Entity = new A();
});
You should not use extensions in the list of dependencies you give to require or define. Modules should be named without extension. So here 'a', not 'a.js'. Using 'a.js' will cause RequireJS to fail loading what you really want once the optimizer has run. Let's say you have a file named a.js which has:
define(function () {
return function () {};
});
The optimizer will include it into your main-built.js file like this:
define("a", function () {
return function () {};
});
Note how the first parameter to define is now "a". This has been added by r.js. This is the name of the module. When you load main-built.js, a module named "a" is defined. When you use require with "a.js", you are telling RequireJS you want something in a file named a.js so RequireJS will go looking for that and ignore what is in main-built.js.
Also, jQuery 1.8 or over does not need a shim.
I just have added
shim: {
'jQuery': {
exports: '$'
}
}
into the build.js file, and it works perfectly !
Thanks !

jQuery file upload and RequireJS configuration

I'm trying to use jQuery file upload, but I'm getting stuck with the RequireJS configuration. We install our dependencies in a /ext/ folder, e.g:
/src
/ext
/jquery-file-upload
In my main.js I use the following config:
require.config({
paths: {
"ext/jquery-file-upload": "../ext/jquery-file-upload/js/jquery.fileupload"
}
});
require([
"ext/jquery-file-upload"
]);
But then RequireJS tries to load jquery.ui.widget.js from the root instead of as a relative file. It is located in the jquery-file-upload directory..
Does anyone know what I'm doing wrong, or does anyone know of a working RequireJS config for jQuery file upload?
Thanks,
Martijn
If you look at the jquery.fileupload.js file, at the top it declares its own dependencies
if (typeof define === 'function' && define.amd) {
// Register as an anonymous AMD module:
define([
'jquery',
'jquery.ui.widget'
], factory);
You need to edit your require.config path for the jquery.ui.widget item.
require.config({
paths: {
'jquery.ui.widget': 'your_path_here/jquery.ui.widget'
}
});
If I don't mistake, your problem is that your plugin - jquery-file-upload - tries to load its dependencies by itself.
The problem is that filepaths in JS are relative to the page loading the script file, not to the file itself (Relative Paths in Javascript in an external file). This explains why your file seems to be loaded relatively to the root and not the given path.
In this case, you probably will have to manipulate a bit of the plugin code and take a look at the explanations given here concerning requireJS and dependencies loading:
How do I use requireJS and jQuery together? .
Try mapping jquery.widget.ui to the correct path...
require.config({
paths: {
"ext/jquery-file-upload": "../ext/jquery-file-upload/js/jquery.fileupload"
"jquery.ui.widget": "../ext/jquery-file-upload/js/vendor/jquery.ui.widget"
}
});
require([
"ext/jquery-file-upload"
]);
For anyone else having this problem...this is the config that worked for us.Hope it helps someone
The paths declaration
paths: {
'jquery.ui.widget': '../../Scripts/FileUpload/jqueryui/jquery.ui.widget',
'jquery_iframe_transport': '../../Scripts/FileUpload/jquery.iframe-transport',
'jquery.fileupload': '../../Scripts/FileUpload/jquery.fileupload'
}
The Define statement
define(['jquery.ui.widget','jquery_iframe_transport','jquery.fileupload'])
The above is dependent on jquery being loaded. We are using Durandal hence dont need a shim but you will need to ensure jquery is loaded before anything else
The initialisation in code
function uploadFile() {
var url = '/Backload/UploadHandler';
$('#fileupload').fileupload({
url: url,
dataType: 'json',
done: function (e, data) {
$.each(data.result.files, function (index, file) {
$('<p/>').text(file.name).appendTo('#files');
});
},
progressall: function (e, data) {
var progress = parseInt(data.loaded / data.total * 100, 10);
$('#progress .bar').css(
'width',
progress + '%'
);
}
});
}
This is the basic upload example..

require js shim not working for this code

I have the following code.
<script src="js/libs/require.js"></script>
<script>
requirejs.config({
baseUrl:'js/modules/',
paths:{
'bbn':'../libs/backbone',
'underscore':'../libs/underscore'
},
shim:{
'bbn':{
exports:'B',
deps:['underscore']
}
}
})
requirejs(['bbn'], function(B){
console.log(B)
});
</script>
The function parameter B is not pointing to Backbone. Instead its getting logged as undefined.
I followed the following post and arrived to this point:
Loading Highcharts via shim using RequireJS and maintaining jQuery dependency
I see both underscore and backbone JavaScript files getting downloaded in firebug.
Underscore is not AMD compat either, so make sure you shim that too:
requirejs.config({
baseUrl:'js/modules/',
paths:{
'bbn':'../libs/backbone',
'underscore':'../libs/underscore'
},
shim:{
'bbn':{
exports:'Backbone',
deps:['underscore']
},
'underscore': {
exports: '_'
}
}
})
requirejs(['bbn'], function(Backbone){
console.log(Backbone)
});
You will see Underscore being downloaded but because it not defined as a proper module RequireJS just treats it as a normal JS file and doesn't get a return value
The latest version of Underscore (~1.6.0 as of writing this) is AMD-compatible. Do not use it as a shim or you may run into issues.

Resources