Requirejs static included script the callback does not get executed - javascript

In a current project i need to use RequireJS as well with dynamic included modules as with static included javascripts.
The static included scripts are needed for every page and get concatinated at the end of the development process. These are for example jQuery jQueryUi and some more scripts like an autosuggest script.
The dynamic part is only used on some pages like a configurator which has an additional script needed that is to big to be loaded on every page. That's why this script is loaded as a module on the page where it is needed.
While jQuery and jQueryUi are playing nice and are available as amd modules due to the how the factory method gets executed:
(function(t){
"function"==typeof define&&define.amd?define('jquery-ui',["jquery"],t):t(jQuery)
})(function(t){...});
This is not true for the autosuggest part. Though i added the functionality in exact the same pattern:
(function(t){
"function"==typeof define&&define.amd?define('suggest',['jquery','jquery-ui'],t):t(jQuery)
})(function(t){
console.log('yeah');
});
The yeah part inside the factory does not get executed.
What am i missing? Is there anything else needed to make it work?

From the RequireJS documentation (http://requirejs.org/docs/api.html):
Ideally the scripts you load will be modules that are defined by
calling define(). However, you may need to use some traditional/legacy
"browser globals" scripts that do not express their dependencies via
define(). For those, you can use the shim config. To properly express
their dependencies.
You can try using something like:
requirejs.config({
...
shim: {
'suggest': {
deps: ['jquery', 'jquery-ui'],
exports: 'suggest'
}
}
...
}
You might need to add sugget to you paths property too.

Related

Mark script as already defined in require js

I had to move some scripts to synchronious load through bundle, and I want to set those scripts as already defined, so require.js would not ask server for them in next calls.
Let me explain:
I had some scripts that were required everywhere, e.g. i18n, and jquery. So I have hundreeds if calls all around the project such as
require(['jquery', 'i18n', 'commonjs', ...
I bundled 'jquery', 'i18n', 'commonjs' into one script core.js which now is inserted in layout <script src="/core.js"></script>
All functions from jquer, i18n now can be accesses globally, without need of requiring them. I want to specifically say to reuire.js that those scripts are already loaded, something wich bundles should do.
I've read article about using bundles and tryied to put
bundle in my config file
bundles: {
'core': ['jquery', 'i18n', 'commonjs']
}
but it doesnt work, there lots of mistakes fallen and as I understood th only way to use bundle is to use r.js which optimizes js and folders.
Actually, all I want is to set some scripts as already loaded for require.js. Is ther a dirty way to do it?
There's no configuration option to mark a script as already defined. What you can do is to call define yourself with a module name and an appropriate return value. For instance, if you load jQuery with a script element before you start loading any module through RequireJS, you can do:
define("jquery", [], function () {
return $;
});
The code above makes it so that whenever any module requires the module jquery, they get the value of $. I normally place such modules like the one just before my call to require.config. It's just a convenient place for them.

Using shim config with almond

I am trying to shim certain modules for usage with almond like so:
<script>
requirejs.config({
shim: {
'jQuery': { exports: 'jQuery' },
//etc.
</script>
as certain scripts will already be included. However, this code:
require(['jQuery', function($) {
});
results in "undefined missing jQuery". If I shim jQuery like this:
define('jQuery', function() {
return jQuery;
});
it works.
I am not building my JS at all, just dropping almond.js into an existing web software so I can develop my new components with AMD. I would like to shim existing globals for my new modules.
I am guessing shims are only resolved on build and that the build does exactly what I am doing above, is that correct?
The name for jQuery is hard-coded to "jquery". If you deviate from this you'll run into trouble. But that's not your only problem.
Using shim is not the same as calling define with a module name. When you use shim like you do in your question you tell the loader that there exist a module with the name jQuery and that once that module is loaded, RequireJS should return as a module value the value of the variable jQuery. The emphasized text is important: the loader will fetch and load a module named jQuery.
The define you show in your question would usually be placed together with the call to require.config, either just before it or just after it. This declares a module named jQuery. Because the module is already there, when the loader needs to get this module, there is nothing to fetch. This is an important difference when it comes to Almond.
Almond has restrictions, one of them is:
optimize all the modules into one file -- no dynamic code loading.
(Emphasis added.) Using the terms I've used in this answer this means "no fetching". When you use your define call, you are fine. When you use the shim, then unless you optimized your modules into one file, the loader has to try fetching the module. Almond cannot do that.

How let users to access requirejs modules outside main?

I'm implementing an AMD module oriented js framework that will be used on third party sites.
With this line, framework users will configure necessary modules
<script data-main="/main.js" src="/require.js"></script>
The problem is that data-main reference is loaded asynchronously so any js logic depending on modules loaded by main.js would fail unless I can be sure that it finished loading.
I'm pretty new to requirejs so not sure what's the good practices to create a framework that will be used by other people.
How could resolve this very simple problem?
EDIT
An example to explain my point
main.js
requirejs.config({
paths: {
jquery: '//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min',
}
});
main js reference + extra code
<script data-main="/main.js" src="/require.js"></script>
<script>
require(['jquery'], function($) {
// since main.js is not loaded yet, it will assume that there is a jquery.js
// file on the same folder that this file
console.log('myModule was also loaded and can use jQuery');
});
</script>
If you want to depend on other libraries and are specifically targeting being in a Require pipeline, all you need to do is to declare some dependencies with
define(
'myModule', // module name
['foo'], // module dependencies
function(foo){ // module construction
var myModule = {};
.... code to set up the module ....
return myModule;
});
and Require will take care of things. This will register your module with Require and won't attempt to build your module until all of your dependencies are available. This functionality is discussed here.
Update with example
Require JS is designed to work both with and without a prebuilt configuration. The paths property of the Require config object only provides Require with information on how to attempt to find libraries which have not yet been registered. However, the registration and then dependency resolution is handled by Require regardless of how/where the module was registered. Please see this JSFiddle for a working example of how you can register and use dependencies.
Update 2 regarding config
Since RequireJS loads everything asynchronously, you are correct, your code example will not work. However, you're making an incorrect assumption about how it is "supposed" to work. You have an incorrect example of what your library's clients' Require configuration will look like. If someone else is building an application using RequireJS and they want to use your library, they should declare the path to your library in their require.config:
require.config({
paths: {
// this tells require how to load jQuery (a library maintained neither by you nor your clients).
'jquery': '//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min',
// this tells require how to load myModule (the library you are building for your clients).
'myModule': '//example.com/js/my-module.min',
// this tells require how to load foo (a library built and hosted by your clients).
'foo': 'scripts/foo'
}
});
On the other hand, if your clients can't update their Require config to include your library in the declarations, then you're out of luck. All you can do is take all of your dependencies and bundle them up in your distribution file and then declare no dependencies:
define(
'myModule',
[], // can't declare any dependencies
function() {
// these dependencies are inside of the definition function to keep the global namespace clean
// since we need jQuery, we have to inline it:
var jQuery = ....
// same goes for d3.js
var d3 = ....
// now we can set up the module itself
var myModule = {};
.... code to set up the module ....
return myModule;
}
);
Obviously, this option means that you can't use the libraries which are being used by your clients. This means your library will be a lot heavier and include effectively duplicate code and data.
Hope that helps you understand how Require works and how other people will use your library.
I've finally used this approach
<script src="http://mydomain/js/require.js"></script>
<script>
requirejs.config({
baseUrl: 'http://mydomain/js'
});
require(['main'],function(){
// here users can do anything they want as all required libraries are loaded
});
</script>
main.js is loaded with a require instruction instead of using data-main attribute from script tag , this provides a callback where users can put their code.

Preserving jQuery dependency for highcharts with requireJS optimizer

I'm testing out requireJS and am trying to make a simple project using highcharts. I've started with the requireJS multipage example project as a starting point.
My dir structure looks the same as the base structure, with highstock.js added in the lib directory.
page1.html: page 1 of the app.
page2.html: page 2 of the app.
js
app: the directory to store app-specific modules.
lib: the directory to hold third party modules, like jQuery.
common.js: contains the requirejs config, and it will be the build
target for the set of common modules.
page1.js: used for the data-main for page1.html. Loads the common
module, then loads app/main1, the main module for page 1.
page2.js: used for the data-main for page2.html. Loads the common
module, then loads app/main2, the main module for page 2.
common.js holds the configuration and I've added a shim for highstock there:
requirejs.config({
baseUrl: 'js/lib',
paths: {
app: '../app'
},
shim: {
"highstock": {
"exports": "Highcharts",
"deps": [ "jquery"]
},
} // end Shim Configuration
} );
I also am using the base build file, with the addition of a line to set common.js as the config file and another to disable minifying.
optimize: "none",
mainConfigFile: '../www/js/common.js',
In apps/main1.js I've added a var HighCharts= require('highstock'); and I then try to use it.
When I run this in the normal build everything works fine. All the dependencies hold and everything loads.
When I attempt to optimize my build, highcharts doesn't receive the jQuery dependency. I think I see why its happening, but I'm not sure how to fix it.
My build creates 3 files, common.js, page1.js, and page2.js.
The relevant parts of the build output:
js/lib/../common.js
----------------
js/lib/../common.js
js/lib/jquery.js
...
js/lib/../page1.js
----------------
js/lib/../page1.js
js/lib/highstock.js
js/app/main1.js
...
My page then references the built page1. When it attempts to load the highstock module it errors out since jQuery has not yet been loaded/isn't accessible.
When I see the built page1 I can see why.
require(['./common'], function (common) {
require(['app/main1']); //highcharts is in main1 in the non-optimized version
});
define("../page1", function(){});
//a few more defines
(function () { // start highcharts module definition HERE
So instead of being defined in the callback after common (including jQuery) has been loaded, its loaded after making the request, but before the callback executes.
My question is, why is this happening there instead of inside the callback (which is where it is loaded in the non-optimized version). I've tried multiple options in the build.js file and config file and I seem to be missing some key concept or small error.
Sorry for the super long question but I felt all the info was necessary. If more info is needed I can post it, or get rid of something superfluous.
Please take look at very simple example which use require js http://jsfiddle.net/wAM3h/
require({
paths: {
jquery: "//cdnjs.cloudflare.com/ajax/libs/jquery/1.8.2/jquery.min",
hchart: [
"http://code.highcharts.com/highcharts",
"http://code.highcharts.com/highcharts-more",
"http://code.highcharts.com/modules/exporting"
]
}
},
['jquery', 'hchart'], function($, hc) {
window.chart = new Highcharts.Chart(options);
});
Not sure you're still involved with the project or not:
I see that you've not defined the path to the highcharts library in the code above. I could not see it even in the repo you mentioned.
And, again, highcharts prevents re-declaration of this namespace, so you must use a different name
- Hence, you must use a different name while shim-ming it
Note: Libraries like highcharts can be safely used in an amd module without using a shim (unless you need explicit access to the object exported by it).
So, your Config File should look like this:
requirejs.config({
baseUrl: 'js/lib',
paths: {
app: '../app',
'highstock-custom-name': 'path/to/highcharts.js'
},
shim: {
"highstock-custom-name": {
... //as is, although not necessary
}
}
});

Require.js is hurting my brain. Some fundamental questions about the way it loads scripts/modules

Let's assume this is my config.js or main.js:
require.config({
// paths are analogous to old-school <script> tags, in order to reference js scripts
paths: {
jquery: "libs/jquery-1.7.2.min",
underscore: "libs/underscore-min",
backbone: "libs/backbone-min",
jquerymobile: "libs/jquery.mobile-1.1.0.min",
jquerymobilerouter: "libs/jquery.mobile.router.min"
},
// configure dependencies and export value aliases for old-school js scripts
shim: {
jquery: ["require"],
underscore: {
deps: ["jquery"],
exports: "_"
},
backbone: {
deps: ["underscore", "jquery"],
exports: "Backbone"
},
jquerymobilerouter: ["jquery", "backbone", "underscore"],
jquerymobile: ["jquery", "jquerymobilerouter", "backbone", "underscore"]
}
});
require(["jquery", "backbone", "underscore", "app/app.min", "jquerymobilerouter", "jquerymobile"], function ($, Backbone, _, App) {
console.log($);
console.log(Backbone);
console.log(_);
$("body").fadeIn(function () {
App.init();
});
});
If I understand correctly, the paths config option allows you to reference scripts, a-la the <script> tag within HTML. Assuming this is the case, do I still need to alias scripts like jQuery with a $ or underscore with a _ in my actual require statement below? It seems strange that I'd have to, given that if you reference jQuery with a standard <script> tag, $ can be used throughout your script automatically. Shouldn't it be the same using the paths?
I'm new to the shim config option, which I understand has replaced the deprecated order! plugin. What does the exports property actually DO? It doesn't seem to create an alias for a script; for example, if I set the exports for underscore to "whatever", and then try to console.log(whatever), it's undefined. So what's the point?
How would scripts like jQuery be properly used "globally?" That is, what's the proper way to be able to use the $ alias within my App.js module, or any other module in my "app" folder? Do I have to require jQuery within every individual module and alias $ every single time? Or is the way I've done it here the proper way?
I'd greatly appreciate any other criticisms of this particular script as well; the documentation for Require.js, in my opinion, leaves much to be desired; things I'd really like to know more about seem to get glossed over and leave me scratching my head.
Just to clear up any confusion around exports, it's assumed that any shim library attaches a property to the global context (window or root), or modifies an already-existing global property (e.g. a jQuery plugin). When requireJS gets the command to load a shimmed dependency, it examines the global context for a property matching the exports value of that shim config, and if it finds it, returns it as the value of that module. If it doesn't find it, then it loads the associated script, waits for it to execute, then finds the global symbol and returns it.
An important fact to remember is that unless the shim config contains an exports value, any init method on that config will NOT be executed. The dependency loader must locate a value for the module (which is what exports specifies) before that module can be initialized, which is why the property is required if there is a shim init for that module.
update: I also need to point out that if the module in question calls define anywhere, any shim config you have for that module will be ignored. This actually caused me some headaches because I wanted to use the shim config to call jQuery's jQuery.noConflict(true) method to un-globify jQuery and keep it scoped to just the modules that require it, but couldn't manage to get it working. (See update at bottom for info on how to easily do this using map config instead of shim config.)
update 2: A recent question on the requireJS google group made me realize that my explanation might be slightly misleading, so I'd like to clarify. RequireJS will only re-use a shimmed dependency if it was loaded via requireJS at least once. That is to say, if you simply have a <script> tag on the hosting page (say, for example, underscore), like this:
<script src='lib/underscore.js'></script>
<script src='lib/require.js' data-main='main.js'></script>
...and you have something like this in your requireJS config:
paths: {
'underscore': 'lib/underscore'
},
shim: {
'underscore': {
exports: '_'
}
}
Then the first time you do define(['underscore'], function (_) {}); or var _ = require('underscore');, RequireJS will re-load the underscore library rather than re-using the previously defined window._, because as far as requireJS knows, you never loaded underscore before. Sure, it can check to see if _ is already defined on the root scope, but it has no way of verifying that the _ that's already there is the same as the one defined in your paths config. For example, both prototype and jquery assign themselves to window.$ by default, and if requireJS assumes that 'window.$' is jQuery when it is in fact prototype, you're going to be in a bad situation.
All of that means that if you mix-and-match script loading styles like that, your page will wind up with something like this:
<script src='lib/underscore.js'></script>
<script src='lib/require.js' data-main='main.js'></script>
<script src='lib/underscore.js'></script>
Where the second underscore instance is the one loaded by requireJS.
Basically, a library has to be loaded via requireJS for requireJS to have knowledge of it. However, the next time you require underscore, requireJS will go "hey, I already loaded that, so just hand back whatever the exports value is and don't worry about loading another script."
This means you have two real options. One is what I would consider an anti-pattern: simply don't use requireJS to express dependencies for global scripts. That is, as long as a library attaches a global to the root context, you'll be able to access it, event if that dependency isn't explicitly required. You can see why this is an anti-pattern - you've basically just eliminated most of the advantages to using an AMD loader (explicit dependency listing and portability).
The other, better option is using requireJS to load everything, to the degree that the only actual script tag you should create yourself is the one that initially loads requireJS. You can use shims, but 95% of the time it's really not that difficult to add an AMD wrapper to the script instead. It might take a little more work to convert all of your non-AMD libraries to be AMD compatible, but once you've done one or two it gets a lot easier - I can take any generic jQuery plugin and convert it to an AMD module in less than a minute. It's usually just a matter of adding
define(['jquery'], function (jQuery) {
at the top, and
return jQuery;
});
at the bottom. The reason I have 'jquery' mapping to jQuery rather than $ is that I've noticed most plugins these days are wrapped in a closure like this:
(function ($) {
// plugin code here
})(jQuery);
And it's a good idea to pay attention to the intended scope. You can certainly map 'jquery' to $ directly though, assuming the plugin isn't expecting to find jQuery instead of $. That's just the basic AMD wrapper - more complex ones generally try to detect what kind of loader is being used (commonJS vs AMD vs regular ol' globals) and use a different loading method depending on the result. You can find examples of this pretty easily with a few seconds on google.
Update: The workaround I used to support using jQuery.noConflict(true) with RequireJS worked, but it required a very small modification to the jQuery source, and I have since figured out a much better way to accomplish the same thing without modifying jQuery. Luckily enough, so has James Burke, the author of RequireJS, who has added it to the RequireJS documentation: http://requirejs.org/docs/jquery.html#noconflictmap
Paths tell require.js where to look when you require that dependency.
For example i have things configured like this:
"paths": {
"jquery": "require_jquery"
},
"shim": {
"jquery-cookie" : ["jquery"],
"bootstrap-tab" : ["jquery"],
"bootstrap-modal": ["jquery"],
"bootstrap-alert": ["jquery"]
},
this means that every time in a module I do
define( ['jquery']
requirejs loads the file require_jquery from the main path instead of trying to load jquery.js. In your case it would load the jQuery source file, which would then be globally available. I personally don't like that approach and for that reason in the require_jquery.js file I do:
define( ["jquery_1.7.2"], function() {
// Raw jQuery does not return anything, so return it explicitly here.
return jQuery.noConflict( true );
} );
which means that jQuery will be defined only inside my modules. (This is because i write Wordpress plugins and so I can include my own version of jQuery without touching the outside version)
Exports (reading from the docs simply should be the name of the module you are using so that it can be detected if loading went correctly. Here is explained. So if you want to set an export for underscore it should be _
jQuery should be global as I explained, if you simply import it the file is executed and jQuery is global
EDIT - to answer the comments.
yes i mean that, you must export $ or jQuery for jQuery and _ for backbone. From what i got from the docs this is needed only in some edge cases and would not be necessary for libraries that declare themselves in the global namespace as jQuery.
I think that requirejs needs them when it has to fallback from loading jQuery from a CDN. i think that requirejs first tries to load jQuery from the CDN, then makes a check to verify that it was loaded correctly by checking that the "exported" variable exists, and if it doesn't it loads it form the local filesystem (if you had configured fallbacks, of course). This is something that it's needed when requirejs can't see a 404 coming back.
jQuery is globally available because it's declared global. If you simply load and execute the jQuery script, you will end up with two globals, $ and jQuery (or you can do as i did and avoid that). Inside the define() function you can alias jQuery to be whatever you want.
define( [ 'jquery' ], function( jq ) {
// jq is jquery inside this function. if you declared it
// globally it will be also available as $ and jQuery
} );

Categories