require js beginner - dependency injection - requirejs

I'm a beginner with require js and I have a doubt here.
My files are structured like below:
root
|____assets
|____bootstrap-3.0.1
|________dist
|____________js
|____________bootstrap.min.js
|________js
|________angulajs.min.js
|________app.js
|________jquery.min.js
|________underscorejs-min.js
|____________app
|________________main.js
My app.js file is like below:
requirejs.config({
"baseUrl": "assets/js",
"paths": {
": "app",
"jquery": "jquery.min",
"jquery-migrate": "jquery-migrate.min",
"twitter-bootstrap": "../bootstrap-3.1.0/dist/js/bootstrap.min",
"underscore": "underscore-min"
},
"shim": {
"twitter-bootstrap": {
deps: [ "jquery" ]
}
}
});
requirejs(["app/main"]);
My main.js is like below:
define([ "jquery", "twitter-bootstrap" ], function($) {
$(function() {
$("#teste").tooltip();
});
});
My question is about the main file and the dependency jquery in the case of bootstrap. If I had declare that the Bootstrap depends on jQuery I still have to include "jquery" in define function?
I tried to remove "jquery" declaration but the dependency was not injected and I received a undefined error.
Thanks for any help!

Doing it like this is really the way to go:
define([ "jquery", "twitter-bootstrap" ], function($) {
$(function() {
$("#teste").tooltip();
});
});
In the code above, it is clear that $ should be bound to the jquery module. If you do this:
define(["twitter-bootstrap" ], function($) {
$(function() {
$("#teste").tooltip();
});
});
You are binding $ to the twitter-bootstrap module. This won't work without further configuration because twitter-bootstrap exports nothing. (It installs itself as a jQuery plugin.) You could work around it by setting your shim like this:
"shim": {
"twitter-bootstrap": {
deps: [ "jquery" ],
exports: '$.fn.tooltip',
init: function ($) { return $; }
}
}
However, I don't recommend this since someone who comes across the code without knowing the context (that is, not knowing the relationship between the jquery and twitter-bootstrap modules) won't readily know what is going on.
In the shim above the exports field is not absolutely necessary but it help RequireJS know when Bootstrap has been loaded. It should be a symbol that does not exist prior to Bootstrap being loaded but exists after it is loaded.

Related

Using bootbox in RequireJS app

I have a sample app.js file with:
requirejs.config({
"baseUrl": "js/lib",
"paths": {
"jquery": "jquery",
"app": "../app",
"bootstrap": "bootstrap/js/bootstrap.bundle",
"bootbox": "bootbox.min"
},
"shim": {
"bootstrap": {
"deps": ["jquery"],
"exports": 'bootbox'
},
"main": { "deps": ["jquery","bootstrap"] },
"bootbox": {
"deps": ["jquery","bootstrap"],
"exports": 'bootbox'
},
}
});
require(['jquery','bootstrap','bootbox'], function($){
$(function(jquery) {
bootbox.alert("bla")
});
});
When I run my page, I can see the correct JS files being grabbed:
...yet my code fails:
bootbox.alert("bla")
Gives:
ReferenceError: bootbox is not defined
I must be missing something simple (again, apologies if this is a newbie error - I'm still trying to get my head around this library)
Don't use shim with Bootbox. If you look at the source code of Bootbox, you'll see it calls define, which registers it as a proper AMD module. The shim option is only for code which is not a proper AMD module.
Now, the define in Bootbox does this:
define(["jquery"], factory);
It sets a dependency on jQuery, but that is wrong, because in fact Bootbox also depends on Bootstrap being present. So we need to fix this. The following shows how you can fix it. You can use a map configuration option so that when Bootbox requires jQuery, it gets Bootstrap. And you set a shim for Bootstrap so that, in addition to having a dependency on jQuery, its module value is the same as jQuery ($).
Without the map setup, there's no guarantee that Bootstrap will load before Bootbox and you'll be facing a race condition: sometimes it'll work, sometimes not.
requirejs.config({
baseUrl: ".",
paths: {
jquery: "//ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min",
bootstrap: "//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min",
bootbox: "//github.com/makeusabrew/bootbox/releases/download/v4.4.0/bootbox.min"
},
shim: {
"bootstrap": {
"deps": ["jquery"],
// We set bootstrap up so that when we require it, the value with get is just $.
// This enables the map below.
"exports": "$"
},
},
map: {
// When bootbox requires jquery, give it bootstrap instead. This makes it so that
// bootstrap is **necessarily** loaded before bootbox.
bootbox: {
jquery: "bootstrap",
},
}
});
require(["jquery", "bootbox"], function($, bootbox) {
$(function(jquery) {
bootbox.alert("bla");
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.5/require.min.js"></script>
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" />

Bootstrap library with requirejs

I am trying to use bootstrap with require js. So far jquery, underscore and boostrap are loading fine, but I am having issues with one library not loading: bootstrap-tagsinput. How can I debug requirejs and see whether this library is loading or not?
Here is my common.js
requirejs.config({
shim: {
'jquery': {
exports: '$'
},
'underscore': {
exports: '_'
},
'bootstrap': {
deps: [ "jquery" ]
},
'bootstrap-tagsinput': {
deps: [ "bootstrap" ]
}
},
baseUrl: "/",
paths: {
'jquery': [
'//ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min',
'jquery/jquery.min'
],
'underscore': [
'//underscorejs.org/underscore-min',
'underscore/underscore-min'
],
'bootstrap': [
'//maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min',
'bootstrap/dist/js/bootstrap.min'
],
'bootstrap-tagsinput': 'bootstrap-tagsinput/dist/bootstrap-tagsinput.min'
}
});
Require.js is invented to handle dependencies between modules, so it is quite clear that you specify those dependencies in the modules where you use it like you said in your own answer already. The function given as the second parameter to define() gets handles to those modules in the order they are given.
As $ is normally used for jQuery library, it is not a good idea to use $ for something else, because this would create a lot of confusion. But normally you need jQuery in a module as well, so your define() call will most probably look like
define['jquery', 'bootstrap-tagsinput'], function($) {
...
});
In this case $ is bound to jQuery, not to bootstrap-tagsinput. It is instead called via the jQuery mechanisms, so you don't need a second parameter in the function.
I figured out that 'bootstrap-tagsinput' needs to be defined where it's being used. For example, in a page that makes use of this library:
define(['bootstrap-tagsinput'], function() {
// js for the page here
});
And in order to auto-load modules that should always be available (eg. bootstrap or jquery) we can do the same.
define(['bootstrap'], function() {
// main.js contents
});

Automatically load postrequisite files

I'm looking to automatically load dependant files, similarly to how deps works however these files need to be loaded AFTER the initially requested.
For example:
require.config({
baseUrl: '/',
paths: {
jquery: '/assets/components/jquery/dist/jquery.min',
highcharts: '/assets/components/highcharts/highcharts.js',
'highcharts.more': '/assets/components/highcharts/highcharts-more.js'
},
shim: {
jquery: { exports: 'jQuery' },
highcharts: { deps: ['jquery'] },
'highcharts.more' : { deps: ['highcharts'] },
}
});
define(function (require) {
var app = require('assets/js/app');
app.start();
});
When requiring highcharts, jQuery will automatically be loaded as a dependant, however Highcharts requires that you load two files.
Does anyone know how I can load postrequisite files, i.e. after loading highcharts to automatically load highcharts.more.
I could simply set highcharts a dependency of highcharts-more and load highcharts-more however hoped there was a cleaner way?
Hope this makes sense?!
Thanks
Gav
Loading highcharts.more and making it a dependency of highcharts is the way to go. You can also hide highcharts.more from your modules by using a map configuration:
map: {
"*": {
highcharts: "highcharts.more"
},
"highcharts.more": {
highcharts: "highcharts"
}
}
The mapping above says "in all modules (*) when highcharts is requested, load highcharts.more instead, but in highcharts.more when highcharts is requested load highcharts".
With this, you can just list highcharts as a dependency in your actual modules.

whats the correct way to define custom builds of vendor libraries in bower_components?

I am using the yeoman webapp generator with requirejs and I have installed canjs using bower.
canjs has a dir structure like the following
app/bower_components/canjs/amd/can.js
app/bower_components/canjs/amd/can/control.js
app/bower_components/canjs/amd/can/control/route.js
etc..
Inside the can.js file is the following.
define(["can/util/library", "can/control/route", "can/model", "can/view/ejs", "can/route"], function(can) {
return can;
});
All of the dependancy files (control.js, route.js) have their dependancies listed inside define() functions.
What I want to do is customise the canjs build and replace "can/view/ejs" with "can/view/mustache". I can get it to work by changing the reference to ejs within the can.js file but that means I'm editing a vendor file inside of bower_components dir.
I have tried to create a mycan.js build within my scripts dir which looks the same as the can.js file (except for the mustache dependency change) in bower_components and then I change the config to look like this.
require.config({
paths: {
jquery: '../bower_components/jquery/jquery',
can: '../bower_components/canjs/amd/can',
etc..
Then I require the mycan module in any of my files that need it.
This will work properly if I comment out the code inside bower_components/canjs/amd/can.js but if I don't comment the file out, it will require both builds (including the can/view/ejs file I didn't want).
In the require docs http://requirejs.org/docs/api.html under usage 1.1, it has an example of
• www/
• index.html
• js/
• app/
• sub.js
• lib/
• jquery.js
• canvas.js
• app.js
and in app.js:
requirejs.config({
//By default load any module IDs from js/lib
baseUrl: 'js/lib',
//except, if the module ID starts with "app",
//load it from the js/app directory. paths
//config is relative to the baseUrl, and
//never includes a ".js" extension since
//the paths config could be for a directory.
paths: {
app: '../app'
}
});
// Start the main app logic.
requirejs(['jquery', 'canvas', 'app/sub'],
function ($, canvas, sub) {
//jQuery, canvas and the app/sub module are all
//loaded and can be used here now.
});
Here they are using a path which is a directory, not a file. The sub module is getting found because it matches app/sub with the app in the paths config.
If I define my own version of can within the main.js file which contains the require.config then it seems to work but then when I go to build the app, it says
tim#machine:~/server/javascript/yoman:ruby-1.9.3: (master)$ grunt
Running "jshint:all" (jshint) task
Linting app/scripts/main.js ...ERROR
[L54:C1] W117: 'define' is not defined.
define('can', [
Warning: Task "jshint:all" failed. Use --force to continue.
Aborted due to warnings.
Elapsed time
default 567ms
jshint:all 124ms
Total 691ms
Whats the correct way for me to make a custom build of vendor libraries within bower_components?
Here is my main.js. This version works but fails when linting.
require.config({
paths: {
jquery: '../bower_components/jquery/jquery',
bootstrapAffix: '../bower_components/sass-bootstrap/js/affix',
bootstrapAlert: '../bower_components/sass-bootstrap/js/alert',
bootstrapButton: '../bower_components/sass-bootstrap/js/button',
bootstrapCarousel: '../bower_components/sass-bootstrap/js/carousel',
bootstrapCollapse: '../bower_components/sass-bootstrap/js/collapse',
bootstrapDropdown: '../bower_components/sass-bootstrap/js/dropdown',
bootstrapPopover: '../bower_components/sass-bootstrap/js/popover',
bootstrapScrollspy: '../bower_components/sass-bootstrap/js/scrollspy',
bootstrapTab: '../bower_components/sass-bootstrap/js/tab',
bootstrapTooltip: '../bower_components/sass-bootstrap/js/tooltip',
bootstrapTransition: '../bower_components/sass-bootstrap/js/transition',
can: '../bower_components/canjs/amd/can'
},
shim: {
bootstrapAffix: {
deps: ['jquery']
},
bootstrapAlert: {
deps: ['jquery']
},
bootstrapButton: {
deps: ['jquery']
},
bootstrapCarousel: {
deps: ['jquery']
},
bootstrapCollapse: {
deps: ['jquery']
},
bootstrapDropdown: {
deps: ['jquery']
},
bootstrapPopover: {
deps: ['jquery']
},
bootstrapScrollspy: {
deps: ['jquery']
},
bootstrapTab: {
deps: ['jquery']
},
bootstrapTooltip: {
deps: ['jquery']
},
bootstrapTransition: {
deps: ['jquery']
}
}
});
define('can', [
'can/util/library',
'can/control/route',
'can/construct/proxy',
'can/model',
'can/view/mustache',
'can/route'
], function(can) {
'use strict';
return can;
});
require(['app', 'jquery'], function (app, $) {
'use strict';
// use app here
console.log(app);
console.log('Running jQuery %s', $().jquery);
});
JSHint is complaining because require is in an external file. All require's functions are defined before your script loads, but because they're not inside the script JSHint thinks they're custom code which you forgot to define. This is an easy fix; add a predef config so that define, require are already passed to JSHint before it starts linting your files.
jshint: {
options: {
// all of your other options...
predef: ['define', 'require']
},
files : ['app/scripts/main.js']
},

How to use hammer.js plugins with require.js

I am trying to use hammer.js plugins with require.js but I have no luck.
Here is my code:
requirejs.config({
paths: {
"jquery": ('__proto__' in {}) ? "lib/zepto" : "lib/jquery",
"hammer-jquery": "lib/jquery.hammer",
"hammer-showtouches": "lib/hammer.showtouches",
"hammer-fakemultitouch": "lib/hammer.fakemultitouch"
},
shim: {
"jquery": {
exports: "$"
},
"hammer-showtouches": {
deps: [ "hammer-jquery" ],
exports: "Hammer.plugins.showTouches"
},
"hammer-fakemultitouch": {
deps: [ "hammer-jquery" ],
exports: "Hammer.plugins.fakeMultitouch"
}
},
waitSeconds: 30
});
requirejs(["jquery","hammer-jquery","hammer-showtouches","hammer-fakemultitouch"], function ($,Hammer) {
$(function(){
if(!Hammer.HAS_TOUCHEVENTS && !Hammer.HAS_POINTEREVENTS) {
Hammer.plugins.showTouches();
Hammer.plugins.fakeMultitouch();
}
});
});
And here is the error:
Uncaught TypeError: Cannot read property 'plugins' of undefined hammer.showtouches.js:7
Uncaught TypeError: Cannot read property 'plugins' of undefined require.js:8
Uncaught TypeError: Cannot read property 'plugins' of undefined hammer.fakemultitouch.js:7
Uncaught TypeError: Cannot read property 'plugins' of undefined require.js:8
I have tried to include Hammer in shim config with no luck (It's not necessary beacause last version of jquery.hammer is AMD compatible).
Your issue appears to be that you didn't include the hammerjs library which is required by the hammerjs jquery plugin.
To fix this problem, you'll need to add to your requirejs path something like 'hammer':'lib/hammerjs/dist/hammer.min',
Additionally, here's some related information on how I used hammerjs with requirejs.
In my case, I wanted it to work with backbone and requirejs. I installed the libraries with bower.
bower install --save hammerjs
bower install --save backbone.hammer
Then, I proceeded to add the RequireJS configuration:
I noticed that jquery.hammer.js is AMD and automatically requires jquery. It also silently depends on Hammer. It modifies jquery to support Hammer. No shim is required.
I noticed that hammerjs is AMD and automatically exports Hammer. No shim is required.
I noticed that backbone.hammer is AMD and automatically requires underscore, backbone, and hammer. It modifies Backbone to support Hammer. No shim is required.
Therefore, my configuration uses paths only (since no shim support is required):
require.config({
'baseUrl':'',
'paths':{
'underscore':'js/lib/underscore-amd/underscore-min',
'backbone':'js/lib/backbone-amd/backbone-min',
'jquery':'js/lib/jquery/jquery.min',
'hammer':'js/lib/hammerjs/dist/hammer.min',
'jquery-hammer':'js/lib/hammerjs/dist/jquery.hammer.min',
'backbone-hammer':'js/lib/backbone.hammer/backbone.hammer'
},
shim:{
'underscore': { exports: '_' },
'backbone': { deps: ['underscore', 'jquery'], exports: 'Backbone' }
}
});
Wrap HammerJS then bind to the global window:
// #path utils/hammer
define([
'hammer'
], function (Hammer) {
window.Hammer = Hammer;
});
Then make whatever plugins or modules which have dependencies on HammerJS depend on utils/hammer.

Resources