Related
I'm using Handlebars with Require.js, but for some reasons, Handlebars is undifined.
My config:
require.config({
paths: {
underscore: "lib/underscore-min", //1.8.3
backbone: "lib/backbone-min", //1.2.3
jquery: "lib/jquery-2.1.4.min", //2.1.4
marionette: "lib/backbone.marionette.min", //2.4.3
handlebars: "lib/handlebars.runtime.amd.min", //4.0.5
shim: {
"underscore": {
exports: "_"
},
"backbone": {
deps: ["underscore", "jquery"],
exports: "Backbone"
},
"jquery": {
exports: "jquery"
},
"marionette": {
deps: ["backbone", "jquery"],
exports: "Marionette"
},
"handlebars":{
exports: "Handlebars"
}
}
});
...and than in the same file:
require(["handlebars"], function(Handlebars){
"use strict";
console.log(Handlebars); //undefined
});
In an other file:
define(["handlebars"], function(Handlebars){
"use strict";
console.log(Handlebars); //still undefined
});
I'm also using precompiled templates, which are working perfectly fine, so I have no idea, what could be the problem.
Thanks in advance!
---- SOLUTION ----
As Rajab pointed out, the problem was that I used "handlebars" instead of "handlebars.runtime" so thanks for his help!
You need to use:
require(["handlebars.runtime"], function(Handlebars){`
instead of
require(["handlebars"], function(Handlebars){`
and also shim is used only for modules that don't support AMD. Shim completely useless in your example. All these libraries support AMD. For example look at 16 line in backbone.js:
define(['underscore', 'jquery', 'exports'], function(_, $, exports) {
or line 1002 in handlebars.runtime.amd.js
define('handlebars.runtime', ['exports', 'module', './handlebars/base' ... etc
All dependencies already inside it. So you need only paths in config:
require.config({
paths: {
underscore: "lib/underscore-min",
backbone: "lib/backbone-min",
jquery: "lib/jquery-2.1.4.min",
marionette: "lib/backbone.marionette.min",
handlebars: "lib/handlebars.runtime.amd.min",
}
}
require(['handlebars.runtime'], function(HandlebarsOrWhateverNameYouWantItStillWillbeHandlebars){
"use strict";
console.log(HandlebarsOrWhateverNameYouWantItStillWillbeHandlebars);
});
that's all.
I am using gulp and requirejs-optimize
My init.js
;
(function(require) {
"use strict";
//Constants
require.config({
paths: {
"angular": "app/libs/angular/angular.min",
"domready": "app/libs/domReady/domReady",
"angular-ui-router": "app/libs/angular-ui-router/release/angular-ui-router.min",
"angular-bootstrap": "app/libs/angular-bootstrap/ui-bootstrap.min",
"App": "app/app",
"angular-animate": "app/libs/angular-animate/angular-animate.min",
"ui-bootstrap-tpls": "app/libs/angular-bootstrap/ui-bootstrap-tpls.min",
"enums": "app/enums"
},
waitSecond: 0,
shim: {
"angular": {
exports: "angular",
deps: []
},
"angular-ui-router": {
deps: ["angular"]
},
"angular-bootstrap": {
deps: ["angular"]
},
"ui-bootstrap-tpls": {
deps: ["angular-bootstrap"]
},
"domready": {
deps: []
},
"App": {
deps: ["angular"]
},
"angular-animate": {
deps: ["angular"],
exports: "angular"
}
}
});
//This module defines all the Global constants for the app
define("CONST", [], {
templatesPath: "views/",
URL: "https://saltjs-nirus.c9.io/service"
});
define(["require", "domready"], function(require, domready) {
domready(function() {
require(["angular", "App", "app/router"], function(angular, app) {
angular.bootstrap(document, ["salt"]);
});
});
});
})(window.requirejs);
and my Build config file is as below
var gulp = require("gulp");
var requirejsOptimize = require('gulp-requirejs-optimize');
gulp.task('scripts', function() {
return gulp.src(["./../ClientApp/init.js", "./../ClientApp/app/libs/domReady/domReady.js"])
.pipe(requirejsOptimize(function(file) {
return {
baseUrl: "./../ClientApp/",
useStrict: true,
optimize: "uglify",
paths: {
"angular": "app/libs/angular/angular.min",
"domready": "app/libs/domReady/domReady",
"angular-ui-router": "app/libs/angular-ui-router/release/angular-ui-router.min",
"angular-bootstrap": "app/libs/angular-bootstrap/ui-bootstrap.min",
"App": "app/app",
"angular-animate": "app/libs/angular-animate/angular-animate.min",
"ui-bootstrap-tpls": "app/libs/angular-bootstrap/ui-bootstrap-tpls.min",
"enums": "app/enums"
},
shim: {
"angular": {
exports: "angular",
deps: []
},
"angular-ui-router": {
deps: ["angular"]
},
"angular-bootstrap": {
deps: ["angular"]
},
"ui-bootstrap-tpls": {
deps: ["angular-bootstrap"]
},
"domready": {
deps: []
},
"App": {
deps: ["angular"]
},
"angular-animate": {
deps: ["angular"],
exports: "angular"
}
}
}
}))
.pipe(gulp.dest('./../build/final.js'));
});
gulp.task("default", ['scripts'])
Problem: When run gulp i am getting below error
ENOENT, no such file or directory
'/home/ubuntu/workspace/ClientApp/domReady.js'
Here the requirejs optimizer is not taking the path option specified in the build-config file, instead its searching for the physical path which is throwing the error. How can i force the optimizer to lookup to the path option specified in the r.js build config and pick up the files from there for optimization.
Finally i figured out to make it work:
I switched from gulp-optimizer to straight Node r.js as Gulp dev have suggested to use the optimizer directly as r.js is not compatible with gulp.
Git Blacklist.json:
"gulp-module-requirejs": "use the require.js module directly",
Below is my config file:
({
mainConfigFile: "./../ClientApp/app/init.js",
name: "init",
baseUrl: "./../ClientApp/app/",
insertRequire:["init"],
findNestedDependencies: true,
out: "./../ClientApp/build/final.js",
wrap: true,
optimize: "uglify",
uglify: {
toplevel: true,
ascii_only: true,
beautify: false,
max_line_length: 1000,
defines: {
DEBUG: ['name', 'false']
},
no_mangle: true
}
});
I am using uglify for minification provided by require.js
Explanation:
mainConfigFile: Use this option to point out to the file where you have defined your paths, shims, deps etc. RequireJs is intelligent enough to pick the config from this declaration for processing(they use regex pattern to filter this part, so be careful. Refer their DOC)
name : An entrypoint module name. Define this where your executin kicks-off. PS: If you have config and EP is the same file, require will detect this and add the name to your defined call.
insertRequire: This will insert a require statement at the end which will trigger the execution of your code. See the DOC.
findNestedDependencies : Instruct require to pick the dependencies from your entrypoint file. Traverse thorough the files and grab the dependencies for final build.
Rest is self explanatory or refer the documentation
Well this saved me.
To use with the gulp build system see gulp shell module, by this you can wireup your build system to automate the complete process.
Refer this answer to know how to achieve this.
Hope this answer helps someone stuck same as me!
My goals are to include the following in my HTML file and have them all work properly:
index.html:
<script type="text/javascript" src="./vendor.js"></script>
<script type="text/javascript" src="./app.js"></script>
$(document).ready(function() {
// jQuery should be available as `window.$`
$(".myclass").doSomethingWithJquery();
}
<div class="row">
<h1 class="col-md-3 col-md-offset-3">
I should be able to use bootstrap, including bootstrap's javascript libraries, in my templates
</h1>
</div>
<!-- I should be able to use app.js, with its various require('module')
statements and attach rendered HTML to the DOM (I'm using React) -->
<div id="attach-app-js"></div>
Therefore:
jQuery should be available as a global $ variable.
Bootstrap's javascript functions should also work, as they're part of vendor.js
My app.js should also work and not re-declare variables like $ that is already declared under global scope via vendor.js.
See the files below. My current solution errors where I have $(".myclass").doSomethingWithJquery(); When my internet is off, I get the error:
Uncaught ReferenceError: $ is not defined
...which makes me think that (maybe) snackbarjs or another module is leaking jQuery to global scope.
Then, when I change the line in vendor.js to: var $ = jQuery = require('jquery'); I get the error:
Uncaught ReferenceError: $ is not defined
but now it's getting called on the line $(document).ready(function.....
Third, if I comment out vendor.js entirely, I get both errors:
Uncaught ReferenceError: $ is not defined
Uncaught ReferenceError: jQuery is not defined
How can I get this frontend setup to correctly shim variables when necessary in the global scope, and not if it's not necessary??
gulpfile.js:
.task('vendor', ['clean'], function() {
var b = browserify(package.paths.vendor);
var packages = getBowerPackageIds();
packages.forEach(function (id) {
var resolvedPath = bowerResolve.fastReadSync(id);
b.require(resolvedPath, {
// exposes the package id, so that we can require() from our code.
// for eg:
// require('./vendor/angular/angular.js', {expose: 'angular'}) enables require('angular');
// for more information: https://github.com/substack/node-browserify#brequirefile-opts
expose: id
});
});
return b
.bundle()
.pipe(source(package.dest.vendor))
.pipe(gulp.dest(package.dest.dist));
})
.task('js', ['clean', 'install'], function() {
var b = browserify(package.paths.app);
// mark vendor libraries defined in bower.json as an external library,
// so that it does not get bundled with app.js.
// instead, we will load vendor libraries from vendor.js bundle
getBowerPackageIds().forEach(function (lib) {
b.external(lib);
});
var w = watchify(b, watchify.args);
var file = package.dest.app,
dest = package.dest.dist;
w = w
.transform(reactify)
.transform(browserifyShim);
w.on('update', rebundle);
function rebundle() {
return w.
bundle()
.on('error', errorHandler)
.pipe(source(file))
.pipe(gulp.dest(dest))
.pipe(shell([
'python manage.py collectstatic --noinput'
], {
cwd: '../'
}))
// TODO: Do I need this?
.pipe(browserSync.reload({stream: true}));
}
return rebundle();
})
/**
* Running livereload server
*/
.task('server', ['clean', 'install', 'vendor', 'js', 'less'], function() {
browserSync({
proxy: "localhost:8000"
});
})
/**
* Compiling resources and serving application
*/
.task('serve', ['install', 'backup', 'clean', 'lint', 'less', 'vendor', 'js', 'server'], function() {
return gulp.watch([
package.paths.js,
package.paths.app,
package.paths.html,
package.paths.less,
package.paths.python
], [
'lint', 'less', browserSync.reload
]);
})
vendor.js:
$ = jQuery = require('jquery');
require('bootstrap');
require('snackbarjs');
package.json (browserify-shim config):
"browserify-shim": {
"jquery": "$",
"bootstrap": {
"depends": [
"jquery:jQuery"
],
"exports": "bootstrap"
},
"jquery-cookie": {
"depends": [
"jquery:jQuery"
]
},
"eonasdan-bootstrap-datetimepicker": {
"depends": [
"jquery:jQuery",
"moment:moment",
"bootstrap:bootstrap"
],
"exports": "$.fn.datetimepicker"
},
"image-picker": {
"depends": [
"jquery:jQuery"
],
"exports": "$.fn.imagepicker"
},
"raven-js": {
"depends": [
"jquery:jQuery"
],
"exports": "raven-js"
},
"parsleyjs": {
"depends": [
"jquery:jQuery"
],
"exports": "parsleyjs"
}
},
"browser": {
"jquery": "./bower_components/jquery/dist/jquery.js",
"jquery-cookie": "./bower_components/jquery-cookie/jquery.cookie.js",
"image-picker": "./bower_components/image-picker/image-picker/image-picker.js",
"eonasdan-bootstrap-datetimepicker": "./bower_components/eonasdan-bootstrap-datetimepicker/src/js/bootstrap-datetimepicker.js",
"moment": "./bower_components/moment/moment.js",
"bootstrap": "./bower_components/bootstrap/dist/js/bootstrap.js",
"raven-js": "./bower_components/raven-js/dist/raven.js",
"parsleyjs": "./bower_components/parsleyjs/dist/parsley.js"
},
Problem
When executing the compiled handlebars templates the global Handlebars object isn't exported. NOTE: the global Backbone object is working.
See, when the code App.templates.todos attempts to execute in the todos.js file it fails because App.templates.todos isn't defined. Well ultimately that's because the third line in the templates.js file can't execute because the global Handlebars object isn't defined.
Why wouldn't that object get defined? What did I do wrong with require.js here?
UPDATE: I've verified that the handlebars.runtime.js file is in fact executing before the templates.js file and so require.js is running them in the right order when loading the todos.js file.
Bower Components
{
"name": "todomvc-backbone-requirejs",
"version": "0.0.0",
"dependencies": {
"backbone": "~1.1.0",
"underscore": "~1.5.0",
"jquery": "~2.0.0",
"todomvc-common": "~0.3.0",
"backbone.localStorage": "~1.1.0",
"requirejs": "~2.1.5",
"requirejs-text": "~2.0.5",
"handlebars": "~2.0.0"
},
"resolutions": {
"backbone": "~1.1.0"
}
}
main.js
/*global require*/
'use strict';
// Require.js allows us to configure shortcut alias
require.config({
// The shim config allows us to configure dependencies for
// scripts that do not call define() to register a module
shim: {
backbone: {
deps: [
'underscore',
'jquery'
],
exports: 'Backbone'
},
backboneLocalstorage: {
deps: ['backbone'],
exports: 'Store'
},
handlebars: {
exports: 'Handlebars'
},
templates: {
deps: ['handlebars'],
exports: 'App'
},
underscore: {
exports: '_'
}
},
paths: {
jquery: '../bower_components/jquery/jquery',
underscore: '../bower_components/underscore/underscore',
backbone: '../bower_components/backbone/backbone',
backboneLocalstorage: '../bower_components/backbone.localStorage/backbone.localStorage',
handlebars: '../bower_components/handlebars/handlebars.runtime',
templates: '../../../templates',
text: '../bower_components/requirejs-text/text'
}
});
require([
'backbone',
'views/app',
'routers/router'
], function (Backbone, AppView, Workspace) {
/*jshint nonew:false*/
// Initialize routing and start Backbone.history()
new Workspace();
Backbone.history.start();
// Initialize the application view
new AppView();
});
todos.js
/*global define*/
define([
'jquery',
'backbone',
'handlebars',
'templates',
'common'
], function ($, Backbone, Handlebars, Templates, Common) {
'use strict';
var TodoView = Backbone.View.extend({
tagName: 'li',
template: App.templates.todos,
...
});
return TodoView;
});
templates.js
this["App"] = this["App"] || {};
this["App"]["templates"] = this["App"]["templates"] || {};
this["App"]["templates"]["stats"] = Handlebars.template({"1":function(depth0,helpers,partials,data) {
From official documentation of RequireJS:
The shim config only sets up code relationships. To load modules that
are part of or use shim config, a normal require/define call is
needed. Setting shim by itself does not trigger code to load.
So first you need to somehow call the Handlebars and after try to use it in templates.js.
I can't seem to figure out how to load Bootstrap via RequireJS. None of the examples that I found worked for me.
Here is my shim:
require.config({
// Sets the js folder as the base directory for all future relative paths
baseUrl: "./js",
urlArgs: "bust=" + (new Date()).getTime(),
waitSeconds: 200,
// 3rd party script alias names (Easier to type "jquery" than "libss/jquery, etc")
// probably a good idea to keep version numbers in the file names for updates checking
paths: {
// Core libsraries
// --------------
"jquery": "libs/jquery",
"underscore": "libs/lodash",
"backbone": "libs/backbone",
"marionette": "libs/backbone.marionette",
// Plugins
// -------
"bootstrap": "libs/plugins/bootstrap",
"text": "libs/plugins/text",
"responsiveSlides": "libs/plugins/responsiveslides.min",
'googlemaps': 'https://maps.googleapis.com/maps/api/js?key=AIzaSyDdqRFLz6trV6FkyjTuEm2k-Q2-MjZOByM&sensor=false',
// Application Folders
// -------------------
"collections": "app/collections",
"models": "app/models",
"routers": "app/routers",
"templates": "app/templates",
"views": "app/views",
"layouts": "app/layouts",
"configs": "app/config"
},
// Sets the configuration for your third party scripts that are not AMD compatible
shim: {
"responsiveSlides": ["jquery"],
"bootstrap": ["jquery"],
"backbone": {
// Depends on underscore/lodash and jQuery
"deps": ["underscore", "jquery"],
// Exports the global window.Backbone object
"exports": "Backbone"
},
"marionette": {
// Depends on underscore/lodash and jQuery
"deps": ["backbone", "underscore", "jquery"],
// Exports the global window.Backbone object
"exports": "Marionette"
},
'googlemaps': { 'exports': 'GoogleMaps' },
// Backbone.validateAll plugin that depends on Backbone
"backbone.validate": ["backbone"]
},
enforceDefine: true
});
and here is how I call Bootstrap:
define([
"jquery",
"underscore",
"backbone",
"marionette",
"collections/Navigations",
'bootstrap',
],
function($, _, Backbone, Marionette, Navigations, Bootstrap){
The error that I get is this:
Uncaught Error: No define call for bootstrap
Any ideas on how to get this resolved?
I found a working example here:
https://github.com/sudo-cm/requirejs-bootstrap-demo
I followed it to get my code to work.
According to that demo, especially app.js, you simply make a shim to catch Bootstrap's dependency on jQuery,
requirejs.config({
// pathsオプションの設定。"module/name": "path"を指定します。拡張子(.js)は指定しません。
paths: {
"jquery": "lib/jquery-1.8.3.min",
"jquery.bootstrap": "lib/bootstrap.min"
},
// shimオプションの設定。モジュール間の依存関係を定義します。
shim: {
"jquery.bootstrap": {
// jQueryに依存するのでpathsで設定した"module/name"を指定します。
deps: ["jquery"]
}
}
});
and then mark Bootstrap as a dependency of the app itself, so that it loads before app.js.
// require(["module/name", ...], function(params){ ... });
require(["jquery", "jquery.bootstrap"], function ($) {
$('#myModalButton').show();
});
Finally, since app.js is the data-main,
<script type="text/javascript" src="./assets/js/require.min.js" data-main="./assets/js/app.js"></script>
Bootstrap's JS is guaranteed to load before any application code.
Bootstrap lib does not return any object like jQuery, Underscore or Backbone: this script just modifies the jQuery object with the addition of new methods. So, if you want to use the Bootstrap library, you just have to add in the modules and use the jquery method as usual (without declarating Bootstrap like param, because the value is undefined):
define([
"jquery",
"underscore",
"backbone",
"marionette",
"collections/Navigations",
"bootstrap",
],
function($,_,Backbone,Marionette,Navigations){
$("#blabla").modal("show"); //Show a modal using Bootstrap, for instance
});
I found it was sufficient to add the following to my requirejs.config call (pseudocode):
requirejs.config({
...
shim: {
'bootstrap': {
deps: ['jquery']
}
}
});
I like to use Require.Js ORDER plugin, what it does? Simply loads all your Libraries in order, in this case you won't get any errors, ohh and bootstrap depends on jQuery, so we need to use shim in this case:
requirejs.config({
baseUrl: "./assets",
paths: {
order: '//requirejs.org/docs/release/1.0.5/minified/order',
jquery: 'http://code.jquery.com/jquery-2.1.0.min',
bootstrap: '//netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min'
},
shim: {
'bootstrap': {
deps:['jquery']
}
}
});
require(['order!jquery', 'order!bootstrap'], function($) {
});