requirejs define modules accessible from all context - javascript

My problem is the following. I'm using different context (require.js ones) to load some widgets as there have different baseUrl. Each widget need some commons modules like text plugin or css plugin so currently I need to define them into the require config through the paths object. But in this case, each plugin will be loaded each time you require a widget as they belong to different context while they are actually the same.
To be more clear, here an example:
app1_require = require.config({context:'app1',baseUrl:'/app1',paths:{text:'../text'}})
app2_require = require.config({context:'app2',baseUrl:'/app2',paths:{text:'../text'}})
In using app1_require then app2_require, I will have the plugin text injected twice into the DOM (two script tag). How can I proceed to set the text module as a global one which can be used within all contexts? Note that I'm also using a require without any context to load some global dependencies.
Thanks in advance ;)

Related

Why does requirejs allow you to specify a string id?

In requirejs it is possible to define an anonymous module or to give it a string id. According to this article, you would normally not use the string id:
You would not normally use the id when you define your module. It is typically used by tools when optimizing a RequireJS application.
I currently define my modules anonymously and use require.config.paths for the mappings. What I don't understand is: why does requirejs allow you to specify string id's if they're not needed?
why does requirejs allow you to specify string id's if they're not needed?
They only are not needed if require can figure out what module it is that just called define. This is the standard when require() did load the script file that contains the module, whose name and path it knows.
However, the optimizer will put multiple modules in a single file, and there needs to be a different way to figure out what modules are define()d. From the docs:
These [names] are normally generated by the optimization tool. You can
explicitly name modules yourself, but it makes the modules less
portable -- if you move the file to another directory you will need to
change the name. It is normally best to avoid coding in a name for the
module and just let the optimization tool burn in the module names.
The optimization tool needs to add the names so that more than one
module can be bundled in a file, to allow for faster loading in the
browser.
I can't answer as to James Burke's motivations, but I can point out instances of its usefulness.
Define your own single page "layer" for testing, using JSBin or JSFiddle. The following code can be easily executed without having to stand up endpoints for each module or to use r.js to create a layer.
define('A',[], function(){ console.log('A loaded');});
define('B',[], function(){ console.log('B loaded');});
define('c',['A','B'], function(){ console.log('C loaded');});
Define a "local override" for troubleshooting. Add a define right before your require to easily preempt a module definition add a new module so you don't have to touch several files while you're working
define('plugin/fancySelect',[], function(){/* ... */});
require([ /* ... */], function(){
// your main application code
});

Understanding dojo's require in combination with diji/layout

I'm trying to understand dojo in combination with dijit layouts. More specifically: do I need to require the layout widgets in JS or is using html data attributes enough?
I've read the Layout documentation and it seems like you need 3 things:
JavaScript: require the layout widgets you want to use
HTML markup
CSS
But it's not clear to me why I would need to require the layout widgets in JavaScript with:
require(["dojo/parser", "dijit/layout/BorderContainer", "dijit/layout/ContentPane"]);
Which is stated in this example.
I've created a demo without requiring "dijit/layout/BorderContainer" and "dijit/layout/ContentPane" and that's working fine.
Is it safe to leave these out of the require statement?
You should require your modules rather than letting the parser load them because ideally you want all of your modules loaded before the parser runs so that you can bundle all of your dependant modules into a layer file.
This means that you can create a layer with all of your common modules so that you don't need to have 100s of tiny .js requests hit your server, and instead you can load a bulkier one.
Also, if you have custom modules you should load all of the dependant widgets inside of the custom module instead of letting the parser detect.
dojo/parser loads all dependencies for you, so you don't need to require them programmatically in JavaScript, but you need them there once you decide to build your code, i.e. put all the dependencies into a single file, which is the reason, why parser warns you, when it loads dependencies for you:
This auto-requiring is also the reason, why parser.parse() returns a promise, it's because it might be asynchronously loading dependencies:
parser.parse().then(function(/* Array */ widgetInstances) {
});

Using ReqiureJS to have local instances of 3rd party libraries

My application's JavaScript uses jQuery and jQuery plugins and running in "hostile" environment I have no control over (this is PHP extension for eCommerce platform). Thus no way to determine whether my jQuery code will be executed before someone will attach his instance of jQuery/plugins (introduced by other extension) or after this or someone will load jQuery dynamically after page rendered.
Basically the problem is that other extension could use jQuery (with plugins) also and just connecting jQuery with tag will not work.
I have a strong feeling that RequireJS might help me to load needed version of jQuery as far as particular versions of jQuery plugins into the encapsulated scope without polluting global scope (so other extensions will still function properly). Then I'll wrap all my code to "require" statements and it will run using it's own set of jQuery and plugins. I tried this and it kind of works (have not tested this in production environment though) but in some weird way. Seems like this question is kind of relevant to problems I have. Also answer suggesting to use AMD-compatible version of jQuery. But what about plugins? I don't think all plugins I use have such versions.
So questions:
Could RequireJS be used to cover such use case (running jQuery+plugins in undefined environment)? If RequireJS could be used there then any example code or explanation of how to do this properly will be greatly appreciated.
If there is no way to cover this with RequireJS what do you think would be best approach to handle issue?
Yes, I believe RequireJS can help you out here. To my knowledge you'll have to do some legwork, though. Take a look at the source of the (as of 2012-08-19) latest jQuery: link. At the bottom you can see that window.jQuery is being set, defining jQuery and $. The define call for AMD loaders happens after that, so jQuery is in the global scope no matter what. What you want to do is guarantee that your own version of jQuery and plugins stay isolated from other jQuery instances and plugins, correct?
Place your custom jQuery and plugins into their own directory (let's call it "jqcustom") and use that directory when specifying dependencies in your scripts' define calls. You'll want to modify your version of jQuery by wrapping it in a define call and returning the jQuery object at the very bottom, taking out jQuery's window.jQuery and window.$ assignments as well as its default define call in the process (these are both at the bottom, remember). Then you'll need to wrap all of your plugins in define calls and specify your own version of jQuery as the dependency. Example:
define(['jqcustom/jquery'], function(jQuery) {
//inside this method 'jQuery' will override any predefined global window.jQuery
//in case any plugins use the $ variable, define that locally too
var $ = jQuery;
//... plugin code ...
});
If you use RequireJS' optimizer, it can do the wrapping of the plugins for you with its shim config options. jQuery plugins work by adding methods to jQuery's prototype object, so I believe as long as you pass the same jQuery (your custom jqcustom/jquery one) to every plugin with your define wrapping, the plugins' extensions will all be set on the same object and be accessible in subsequent define calls specifying your custom jQuery or custom plugins as dependencies. Good luck!

requirejs loading async templates module pattern

I just decided to try require.js for the first time and for the most part, it works fine - until I get to the point where I try to handle template loading dynamically.
basically, i have a requirement that the app should not proceed until the templates have all been loaded and made available.
to that effect, a module 'templating' has been created. it imports a definition of an array of templates (already available) that it needs to load - require - before it returns.
I am pretty sure this is probably an anti-pattern so how would you fix it?
app -> requires "templating"
define templating ->
- loop through an array of templates and dynamically create a list
- define all templates (via text!) so later we can require("template-name")
- also tried, require all templates
What I observe is that the templating module loads and becomes available to the app before all the templates are loaded.
The XHR fetching the templates follows afterwards.
How do i prevent the module from returning before all the texts load and compile? pseudo code or links to examples would be fine.
We actually had one solution early on in a project and then moved to another one.
[1] Synchronization - Because we're using jQuery in our app we have the jQuery Deferred object available for use throughout the app. I created one instance of Deferred which only had its .resolved() called when all of my templates were loaded. Then each time I tried to use a template I had it wrapped in code like this:
$.when(cache.templatesLoadedPromise).done(
function () {
// Render the view.
$("...").html($.tmpl(cache.template, jsonData));
});
Then none of the renders would happen until the templates were available.
[2] Per module loading and registration - Nobody was that crazy about solution [1] and we wanted each module to load only the templates needed for that particular module. So we started just listing them as "text!something.tmpl" requirements and then doing a registration of that particular template as the first line within the module that had that as a requirement. In our case, we use Handlebars as the template engine together with ICanHandlebarz (similar to ICanHaz for Mustache). So I have a call to ich.addTemplate("something", something); up at the beginning of the module code.
In a few cases, multiple modules used the same templates so I had to test to see if the template was already registered before doing the registration.
I hope one of those would help you with your problem.

drupal_add_js() in module_init() - Will the JS library be loaded across all modules?

When drupal_add_js() is called in the module_init() function, will the loaded JS library have global presence with regards to access to the library by other modules?
Example:
Let's say that there are two modules - each requires one JS library, and both JS libraries are identically named. The JS libraries exist in their respective module directories. Though both JS libraries are identically named, their contents differ such that each module cannot use the other module's JS library - doing so will cause the module to fail.
A problem will thus arise IF in fact JS libraries are loaded with global presence following the above-stated method. When the first module is loaded, its JS library will be applied globally across all modules. Next, the second module is loaded and its JS library will be applied globally across all modules. At this stage, both modules are active. Since the JS libraries are identically named, the second module's JS library would effectively replace the first module's JS library thus causing the first module to fail.
Thanks!
The JavaScript code that a module adds with drupal_add_js() is added to the page, and the list of the JavaScript code added to a page is one, and global for every module. This means that the JavaScript code added from a module is visible to every module.
In fact, that list is contained in a static variable used by drupal_add_js(), and the JavaScript code is added to the page in the page.tpl.php file using the following code:
<?php print $styles; ?>
<?php print $scripts; ?>
$scripts is initialized in template_preprocess_page() with the following code, which returns a string containing the HTML to use for the <script> tags basing on the content of that static variable.
$variables['styles'] = drupal_get_css();
$variables['scripts'] = drupal_get_js();
If for example, two modules calls the function with drupal_add_js(drupal_get_path('module', 'first_module') . '/jquery_plugin_tree'), and drupal_add_js(drupal_get_path('module', 'second_module') . '/jquery_plugin_tree'), then the page will contain two <script> tags that point to two files, even when the content of the file is the same in both the cases.
In the case two modules use the same path for the JavaScript file, then there will be just a <script> tag added.

Categories