require.js, am I doing it right? - javascript

I'm new to require.js and found the documentation quite hard to understand. After a while I got my project up and running with the following setup.
project
|
|--js
|--vendor
|--require.js
|--modernizr.js
|--jquery.js
|--modules
|--module1.js
|--module2.js
|--main.js
|--index.html
index.html:
<!DOCTYPE html>
<html>
<head></head>
<body>
<script data-main="/js/main.js" src="/js/vendor/require.js"></script>
</body>
</html>
main.js
define([
"vendor/modernizr",
"modules/module1",
"modules/module2"
]);
modules/module1.js
define(['vendor/jquery'], function () {
// Some module code like
$('#button').on('click', function(){});
});
Is this a good setup if I like to have one import file (main.js) like in a less setup for CSS?

This setup is generally in the right direction, just some slight modifications need to be made.
For the modules, you need to pass in a matching number of arguments for each dependency of the module. In this case, you would want to assign $ to what is returned by the jQuery module, so that you can actually use $ within the module:
define(['vendor/jquery'], function ($) {
// Some module code like
$('#button').on('click', function(){});
});
For main.js, the define() call should be a require() call so that you will be executing whatever's in the module instead of simply registering it a module for some other module to execute:
require([
"vendor/modernizr",
"modules/module1",
"modules/module2"
], function(Modernizr, module1, module2) {
// do something with Modernizr, module1, module2
});

Related

Why is my global requirejs var undefined?

I have two seperate applications. Both use jQuery an RequireJS. I want to embed application A into application B, so I transfer the required HTML to application B. Application B is an instance of edx-platform (https://github.com/edx/edx-platform).
So far so good, but the second app won't load. First I had both script tags with data-main, but it doesn't work. So I searched and I found this: https://stackoverflow.com/a/10839885
I looks good so far, but my global requirejs variable is undefined. In the HTML edx-platform calls RequireJS with this code:
<script>
window.baseUrl = "/static/";
(function (require) {
require.config({
baseUrl: window.baseUrl
});
}).call(this, require || RequireJS.require);
</script>
<script type="text/javascript" src="/static/lms/js/require-config.js"></script>
<script type="text/javascript">
(function (require) {
require.config({
paths: {
'js/courseware/courseware_factory': 'js/courseware/courseware_factory',
'draggabilly': 'js/vendor/draggabilly',
'js/courseware/toggle_element_visibility': 'js/courseware/toggle_element_visibility',
'js/courseware/course_home_events': 'js/courseware/course_home_events',
'js/courseware/link_clicked_events': 'js/courseware/link_clicked_events',
'moment': 'common/js/vendor/moment-with-locales',
'moment-timezone': 'common/js/vendor/moment-timezone-with-data',
'js/student_account/logistration_factory': 'js/student_account/logistration_factory',
'js/groups/views/cohorts_dashboard_factory': 'js/groups/views/cohorts_dashboard_factory',
'js/dateutil_factory': 'js/dateutil_factory',
'js/courseware/accordion_events': 'js/courseware/accordion_events',
'js/bookmarks/views/bookmark_button': 'js/bookmarks/views/bookmark_button',
'js/views/message_banner': 'js/views/message_banner',
'js/student_profile/views/learner_profile_factory': 'js/student_profile/views/learner_profile_factory'
}
});
}).call(this, require || RequireJS.require);
</script>
My code is included later. I tried to run a simple console.log(requirejs) but it didn't work. requirejs is also undefined when calling it from the developer console.
OK I found out why it is undefined. edX also loads the file which undefined all of those variables.
Now I need to find a way to get my code running.

How to access modules bundled from webpack outside bundles/chunks

Let me first say that I have tried searching for an answer to this and haven't found anything that works or even hints to it being possible.
I recently moved from RequireJS configuration to rolling up with Webpack. In some places in our Groovy app we have have a script tag in a GSP that will access modules through RequireJS like so:
<html>
<body>
...
<script>
require([
'jquery',
'customModule'
], function($, customModule){
// do something with jquery
// ...
// do something with customModule
});
</script>
</body>
</html>
I would need to be able to do the equivalent with Webpack for a few reasons, the most important is our ability to inject Javascript from our admin side to hotfix any production bugs without pushing a maintenance release.
Expecting I should be able to do something like this:
<html>
<body>
...
<script>
webpack_require([
'jquery',
'customModule'
], function($, customModule){
// do something with jquery
// ...
// do something with customModule
});
</script>
</body>
</html>
If you know how to access Webpack bundles from the outside please share! Thanks

Can we use requirejs in an angular application to manage/modularize only a part of the application?

I have an existing angular web app which doesn't use require.js. I have to create a new business module in the existing application. Can I use require.js for the new module only? So that I don't have to touch the existing code?
The existing index.html looks like this:
<html>
<head>
...
</head>
<body>
...
<script src="http://cdn.gse.site/angular/1.2.9/angular.js"></script>
<script src="js/angular-ui-router.js"></script>
<script src="js/services/angDashboardService.js"></script>
<script src="js/controllers/angDashboardController.js"></script>
<--- More custom scripts here --->
</body>
</html>
I tried including require-main.js in the existing index.html file without removing any of the existing script tags.
The require-main.js looks like this :
require.config({
baseUrl: 'js',
paths:{
'angular' : '...'
},
shim: {
'angular': {export: 'angular' },
'new-module': {
deps: ['angular'], export: 'new-module'
}
}
});
require(['new-module'], function(){});
I am getting the error as following:
Uncaught Error: [ng:btstrpd] App Already Bootstrapped with this Element '<body class="preload ng-scope" ng-app="angDashboard">'
Can we use requirejs in an angular application to manage/modularize only a part of the application?
Yes you can (but why...?). You will be hits by some serious headache if you are not ware of what are you doing
Anyways to able to do this you must be fully understand the concept of angularJS and requireJS .
<script src="http://cdn.gse.site/angular/1.2.9/angular.js"></script>
<script src="js/angular-ui-router.js"></script>
<script src="js/services/angDashboardService.js"></script>
<script src="js/controllers/angDashboardController.js"></script>
This mean you already had an angular app running. So you will not (should not) config or load angular anymore with requireJS
require.config({
baseUrl: 'js',
paths:{
'async-module' : '...'
},
// You won't use 'shim' with this structure
// shim: {}
});
async-module.js
// assuming somewhere you have did this
// var app = angular.module([...]);
//
// NOW you need to convert this 'app' to global variable. So you can use it it requirejs/define blocks
// window.app = angular.module([...]);
define(function(){
window.app.controller('asyncCtrl', function($scope){
// controller code goes here
});
});
Then somewhere inside your app, when you want to load this async-module
requirejs(['async-module'], function(){
console.log('asyncCtrl is loaded!');
});
SUMARY >>
It is possible to do what you asked but it does not very effective. And will be like hell in code management.
If this answer took you lesser than 5 mins to understand, you can give it a try.
If it took you longer than 5 mins to understand. I am highly not recommending you to do this. Using requireJS with angularJS in common way (everything loaded by requireJS) is already complicated and tricky. And this use case even beyond that.

RequireJS seems to be ignoring my path definitions ( I'm not calling require() in the html). Why?

I've been researching this question a lot. I know a common issue is caused with asynchronous loading before require has been defined. I dont think that's the issue that I'm experiencing. I'm declaring baseURL and paths in a config file, but I always get an error that the scripts can't be found because it seems to ignore my paths and just look from the baseURL.
This is my folder structure
index.html
js/
app/
jsonLoader.js
notionalAddGraph.js
lib/
config.js
require.js
There's nothing in lib, but I'm mimicking the structure of a project that I'm working on. Here is the html for the page.
HTML:
<html>
<head>
<title>Blah Blah Blah</title>
<script data-main="js/config.js" src="js/require.js"></script>
<script id="graphData" type="application/json">
{
"json": "Is better than",
"global": "but it requires a change to the authoring",
"environment": "also, because of the JSON.parse",
"required": "it is probably slower too"
}
</script>
</head>
Here is the code for config.js
config.js:
require({
baseUrl: 'js/lib',
paths: {
app: '../app'
},
urlArgs: 'bust=' + (new Date()).getTime()
});
require(['app/jsonLoader','app/notionalAddGraph'],
function(require) {
'use strict';
});
and here is the code for jsonLoader.js
jsonLoader.js:
define(function() {
var jsonString = document.getElementById('graphData').textContent;
return JSON.parse(jsonString);
})
annnd here is the code for notionalAddGraph.js
notionalAddGraph.js:
define([
'jsonLoader'
], function(json) {
console.log(json.required);
});
so every time that I try this, require tries to load js/lib/jsonloader.js and fails because it should be looking under app, not lib. Oddly enough, if I intentionally specify the incorrect path when I define app, requireJS will at least look for jsonLoader where I told it to. Obviously though, its not there, because I specified the wrong path. When I specify the correct path, it doesn't work and tries to load from baseURL. I've tried changing everything I can imagine for config.js, but nothing seems to work. I've copied the format directly from requireJS API, but still nothing. Does anyone have an idea as to what I might be doing wrong?
In notionalAddGraph.js you have define with 'jsonLoader' so its looking in the correct path as expected, add ../ to your define and should work correctly
Here's what i'd try
First, call the scripts using an absolute path (you can rollback this change later, but it's just to be on the safe side)
<script data-main="/js/config.js" src="/js/require.js"></script>
Second, declare the config parameters using requirejs.config. I guess that the way you're doing now they are just variables.
requirejs.config({
baseUrl: 'js/lib',
paths: {
app: '../app'
},
urlArgs: 'bust=' + (new Date()).getTime()
});
Third, the return of the jsonLoader isn't the require object, but the object that comes from parsing the jsonString. The way you're doing now is replacing the require object.
require(['app/jsonLoader','app/notionalAddGraph'], function(jsonObject) {
'use strict';
console.log('require is still', require, 'and jsonobject is ',jsonObject);
});

play 2.2 webjars requirejs jquery integration

After upgrade to play 2.2, I cannot make requirejs work as before.
It seems to me that the requirejs is not initialized or configured correctly. I tried to follow https://github.com/mariussoutier/play-angular-require-seed, but I cannot even make the simplest case to work.
jquery can be located correctly in main.js, but require(['jquery'].....) does not work in tag.
If someone could help me with this, it would be appreciated a lot.
webjars definition
libraryDependencies ++= Seq(
javaJdbc,
javaEbean,
cache,
"org.webjars" % "jquery" % "1.10.2",
"org.webjars" % "requirejs" % "2.1.1",
"org.webjars" % "webjars-play_2.10" % "2.2.0"
)
resolvers += "typesafe" at "http://repo.typesafe.com/typesafe/repo"
requireJs += "main.js"
requireJsShim += "main.js"
main.js
(function(requirejs) {
"use strict";
// -- PROD RequireJS config --
requirejs.config({
shim: {
"jquery": { exports: "$" }
},
paths: {
"jquery": ["/webjars/jquery/1.10.2/jquery.min"]
}
});
// It works fine here
require(["jquery"], function($) {
console.log($);
});
})(requirejs);
index.scala.html
#(title: String)
<html>
<body>
<script src='/lib/require.js' type='text/javascript' data-main="/assets/javascripts/main"></script>
<script type="text/javascript">
require(["jquery"], function($) {
console.log($);
});
</script>
</body>
</html>
main.js is loaded, and the error message in the console is
Uncaught TypeError: Property 'require' of object [object Object] is not a function
Edit
The key here is to understand the AMD concept. Although two snippets in tag are placed one after the other, they are not guaranteed execute in order. Therefore, when the second snippet runs, the requirejs may not be configured yet.
<script src='webjars/requirejs/2.1.8/require.js' type='text/javascript' data-main="javascripts/main"></script>
<script type='text/javascript'>
// Second snippet
require(['jquery'], function($) {return $;});
</script>
To fix this, we have two different approaches:
Include the javascript code of the second snippet in main.js (specified in data-main) and Use require(['dependencies'], function() {// do second.js})
Do not specify data-main field when including requirejs, and do all the configuration in the second part.
IMHO, the first approach is preferred and is the exact one used in the sample project https://github.com/mariussoutier/play-angular-require-seed
Qualifying my reply, definitely not an expert...
In your index.scala.html page, you're trying to use a script that's loaded asynchronously. That will give you random results, because it might very well not be loaded in time.
From the RequireJS site:
Note: the script tag
require.js generates for your data-main module includes the async
attribute. This means that you cannot assume that the load and
execution of your data-main script will finish prior to other scripts
referenced later in the same page.
For example, this arrangement will fail randomly when the
require.config path for the 'foo' module has not been set prior to it
being require()'d later:
<script data-main="scripts/main" src="scripts/require.js"></script>
<script src="scripts/other.js"></script>
// contents of main.js:
require.config({
paths: {
foo: 'libs/foo-1.1.3'
}
});
// contents of other.js:
// This code might be called before the require.config() in main.js
// has executed. When that happens, require.js will attempt to
// load 'scripts/foo.js' instead of 'scripts/libs/foo-1.1.3.js'
require( ['foo'], function( foo ) {
});
Also of note... in the example you linked to, this problem is avoided, because all the usage of "require" happens in main*.js
So what I believe you need to do, is to wrap JavaScript code contained within your second script in an event handler which is listening for an event such as window.onload or DOMContentLoaded event, to delay the execution until those events have fired (and all will be present and correct).
References:
http://requirejs.org/docs/api.html#data-main
Javascript async loading and order of execution
https://github.com/mariussoutier/play-angular-require-seed/blob/master/app/views/index.scala.html

Categories

Resources