requirejs dynamically calling custom modules - javascript

I'm trying to dynamically load custom modules based on a data attribute. It's almost working perfectly but for some reasons my module is called twice with different paths and I can't figure why.
My project structure looks like that:
+ Assets
+ js
• main.js
+ libs
+ modules
• mod.env.js
• mod.utils.js
• mod.session.js
+ plugins
+ views
• signup.js
• login.js
• home.js
In my main.js file I have some basic configuration:
require.config({
baseUrl: '/Assets/js',
paths: {
// Libs
'jquery' : 'libs/jquery/jquery-2.0.3.min',
// Module for the project
'env': 'modules/atmco.env',
'utils': 'modules/atmco.utils',
'session': 'modules/atmco.session'
}
});
Still in the main.js file is where I put the logic for the conditial loading of the modules:
require(['require', 'jquery','env'],
function ( require, $, env ) {
'use strict';
function init() {
// Grab the modules/pages on the data attribute of the body
var modules = $('body').data('modules') || '';
var pages = $('body').data('page') || '';
// Initialize the environment stuff for your project
env.initEnv();
if ( pages ) {
require(['./views/' + pages.toLowerCase().split(/\s*,\s*/)[0]]);
}
}
// Initialize the application when the dom is ready
$(function () {
init();
});
}
);
My page has the right attributes (<body data-page="Signup" data-module="">) but for some reasons requirejs tries to call 2 different files:
The custom module is called as expected
"/Assets/js/views/signup.js"
Then it tries to call "/Assets/js/signup.js" which doesn't exists
Finally, here's a look at how I define my custom module, including the custom name. It seems pretty basic:
define('view/signup',
['utils'],
function ( utils ) {
console.log('my module' + utils.lang );
return {
};
}
);
If anyone could point me to my mistake it would really help me with my app and understanding better how requirejs works. Thanks a lot!

Found the solution:
Naming (or naming in fact) the module was actually messing the module definition. Therefor I just needed to adjust the code to:
define(
['utils'],
function ( utils ) {
console.log('my module' + utils.lang );
return {
};
}
);
This question really helped me out figuring it:
Requirejs function in define not executed

Related

How to properly load local AMD modules with jspm/system.js

I am having a really hard time using local AMD modules in an Aurelia project that uses es6, JSPM, and system.js. I am hoping someone out there can help me configure my project to enable me to import/load my AMD modules and use them in my project. The local AMD style modules are in a format similar to the following:
define
(['require',
'lib/alerts/STARTSTOP',
'lib/alerts/STOPPED',
...
],
function( require, STARTSTOP, STOPPED, ... ) {
return {
alert: function( data ) {
var type = data.type;
var ev = data.event;
var cls = require( 'lib/alerts/' + ev );
return new cls( data );
}
};
});
When I try to import/load this module into an es6 module I am running into the following error: Uncaught TypeError: Unexpected anonymous AMD define.. I get this error when trying to load the module in either of the following ways:
System.import('lib/alerts/AlertFactory').then( (m) => {
console.log( m );
});
or
import AlertFactory from 'lib/alerts/AlertFactory.js';
I have also made sure to add the following script to my index.html:
<script>
window.define = System.amdDefine;
window.require = window.requirejs = System.amdRequire;
</script>
In addition to the above I have also added a meta format property to my config.js file, but that hasn't seemed to help either.
meta: {
...
"lib/alerts/*.js": {
"format": "amd"
}
}
Does anyone have any ideas on why I am running into the error I am seeing and how to properly load my modules? I appreciate any help/insight you can offer.
UPDATE
I finally realized that the main issue here is that I'm trying to use existing AMD modules in and Aurelia project, and the default Aurelia gulp build assumes that all code is written in ES6 and not mixed with AMD. That's why I'm having issues. Vanilla jspm/system.js handle a mix of module formats, but Aurelia does not out of the box.
Just put your AMD modules out of src so babel will not be able to transpile it. Here is working solution I use to import jquery modules:
First, I have local_packages folder in project root and I have jquery module local_packages/somelib/js/mymodule.js
Then in config.js
paths: {
...
"local/*": "local_packages/*",
}
map: {
...
"somelib": "local/somelib",
"somelib1": "/local_packages/somelib1",
}
And finally my import looks like: import 'somelib/js/mymodule';

RequireJS + Angular: Undefined app. Callback doesn't shoot

I got main.js with this simple code:
'use strickt';
require.config({
paths: {
'angular' : 'libs/angular' ,
'angular-router' : 'libs/angular-route' ,
},
shim : {
'angular' : {
exports : 'angular'
},
'angular-router' : {
deps: ['angular']
}
}
});
require(['app'], function (mainApp) {
console.log(mainApp);
});
As you can see, I try to fetch app inside require callback. But all I got its undefined.
Here what I got inside app.js file:
define('mainApp', ['angular', 'angular-route'], function (angular) {
var app = angular.module('mainApp', ['ngRoute']);
console.log('should be fired from app.js');
return app;
});
So the question:
Function argument 'mainApp' as undefined inside main.js callback seems logical because console.log() inside app.js doesnt shoot. Can somebody tell me what is wrong with it?
Just remove module name in app.js file (or change it to 'app'). You app.js file will look like:
define(['angular', 'angular-route'], function (angular) {
var app = angular.module('mainApp', ['ngRoute']);
console.log('should be fired from app.js');
return app;
});
http://requirejs.org/docs/api.html#modulename
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.

how to access config.paths after it is defined in main.js

Here is my main.js
requirejs.config({
baseUrl: '/js',
paths: {
jquery: 'jquery',
ckeditor: 'ckeditor/ckeditor',
juiAutocomplete: 'jquery-ui-1.10.4.custom',
tags: 'bootstrap-tokenfield',
createPost: 'createPost',
domReady: 'domReady',
test: 'dropUpload'
},
shim: {
createPost: {
deps: ['domReady!']
}
},
deps: ['require'],
callback: function(require) {
'use strice';
var moduleName = location.pathname.replace(/\//g, '-').replace(/^-/, '');
console.log('moduleName is: ' + moduleName);
console.log('yes is: ' + require.config);
}
});
In the callback, I'd like to access the paths which is defined in the requirejs.config() above. If it is possible, how to do it?
My purpose is to see if a module path is defined(exists). If so, then load the module script. If not checked, then a loading error will generate in the console.
Here are the available methods in requirejs by this console command. I can't find a way to access the paths I defined in requirejs.config(). Is this the right direction?
for (var i in requirejs) {console.log(i);}
config
nextTick
version
jsExtRegExp
isBrowser
s
toUrl
undef
defined
specified
onError
createNode
load
exec
undef
There is no public API to get the whole RequireJS configuration from inside a module. You can have a config section in your configuration, which modules may access.
However, the problem you describe trying to solve does not require you to read the configuration. Calling require the normal way will load the module. If the module can't be loaded, it will generate an error on the console. Presumably you also want your code to know whether the loading was successful or not. You can do it with an errback:
require(['foo'], function (foo) {
// Whatever you'd like to do with foo on success.
}, function (err) {
// Whatever you'd like to do on error.
});
If for some reason you must read the config directly then it is located at requirejs.s.contexts.<context name>.config where <context name> is the name of the RequireJS context. The default context is named _ so the configuration for it would be requirejs.s.contexts._.config. However, this is not part of the public API and can change at any time.

How can I use a local file during Require.js optimisation, but a CDN-hosted version at runtime?

My page includes several components that exist as separate AMD modules. Each of these components is turned into a single file by the Require.js optimiser. Because several of these components share dependencies (e.g. jQuery and d3), the optimiser paths config uses CDN URLs for those dependencies, rather than bundling them into the optimised file.
Here's where it gets tricky. I've written a module loader plugin for Ractive.js called rvc.js, which allows me to include Ractive components that are defined in HTML files. (Yes, I'm asking for help on how to use my own library.)
This works fine - code like this gets optimised as you'd expect:
define( function ( require ) {
var ChartView = require( 'rvc!views/Chart' );
var view = new ChartView({ el: 'chart' });
});
Because Ractive is used by several of the components, it should be served from a CDN like jQuery and d3. But it's used by rvc.js during the optimisation process, which means that the Ractive entry for the optimiser's paths config can't point to a CDN - it has to point to a local file.
Is there a way to tell Require.js 'use the local file during optimisation, but load from CDN at runtime'?
So here's the solution I eventually settled on. It feels somewhat kludgy, but it works:
Stub out the loaders and the library you don't want bundled
Add an onBuildWrite function that rewrites modules depending on the library, so that they think they're requiring something else entirely - in this case Ractive_RUNTIME
Add an entry to your runtime AMD config's paths object, so that Ractive_RUNTIME points to the CDN
My optimiser config now looks like this:
{
baseUrl: 'path/to/js/',
out: 'build/js/app.js',
name: 'app',
optimize: 'none',
paths: {
'amd-loader': 'loaders/amd-loader',
'rvc': 'loaders/rvc',
'Ractive': 'lib/Ractive'
},
stubModules: [ 'amd-loader', 'rvc', 'Ractive' ],
onBuildWrite: function ( name, path, contents ) {
if ( contents === "define('Ractive',{});" ) {
// this is the stub module, we can kill it
return '';
}
// otherwise all references to `Ractive` need replacing
return contents.replace( /['"]Ractive['"]/g, '"Ractive_RUNTIME"' );
}
}
Meanwhile, the script that loads the app.js file created by the optimiser has a config entry that points to the CDN:
require.config({
context: uniqueContext,
baseUrl: baseUrl,
paths: {
'amd-loader': 'loaders/amd-loader',
'rvc': 'loaders/rvc',
'Ractive': 'lib/Ractive',
'Ractive_RUNTIME': 'http://cdn.ractivejs.org/releases/0.3.9/Ractive.min'
}
});

requireJS an entire folder

Is it possible to "require" an entire folder using requireJS.
For example, I have a behaviors folder with a ton of behavior js files. I'd really like to be able to simply use require(['behaviors/*'], function() {...}); to load everything in that folder rather than having to keep that list up to date. Once compressed and optimized I'd have all those files lump together, but for development it's easier to work with them individually.
javascript in browser has no filesystem access and so it can't scan a directory for files. If you are building your app in a scripting language like php or ruby you could write a script that scans the directory and adds the file names to the require() call.
I don't know if I can recommend this approach anymore. I think the more explicit way to do this is by manually "requiring"/"exporting" the functionality you need. The exception I think is if you have a "namespace" of files that you want exported see below "Babel and ES6 Module Import Declarations (export-namespace-from) or see below "Babel and ES6 Module Import Declarations.
These solutions also assume that you have a meaningful file structure - where file names become part of that "require" * definition.
However, if you still need to do this there are a few existing tools and methods that might provide the behavior that you're looking for.
Possible Solutions
Babel and ES6 Module Import Declarations (plugin-export-namespace-from)
Have a setup that is ES6 compliant.
You need to update your .babelrc file to include babel-plugin-proposal-export-namespace-from.
Use export namespace plugin by writing syntax like the following:
common/index.js
export * from './common/a'; // export const a = false;
export * from './common/b'; // export const b = true;
main.js
import { a, b } from './common';
console.log(a); // false
console.log(b); // true
Babel and ES6 Module Import Declarations (plugin-wildcard)
Have a setup that is ES6 compliant.
You need to update your .babelrc file to include babel-plugin-wildcard.
Use wildcard namespace plugin by writing syntax like the following:
main.js
import { a, b } from './common/*'; // imports './common/a.js' and './common/b.js'
console.log(a); // false
console.log(b); // true
RequireJS (Now Outdated)
Download and install require-wild npm install require-wild
Configure the declaration as follows
grunt.initConfig({
requireWild: {
app: {
// Input files to look for wildcards (require|define)
src: ["./**/*.js"],
// Output file contains generated namespace modules
dest: "./namespaces.js",
// Load your require config file used to find baseUrl - optional
options: { requireConfigFile: "./main.js" }
}
}
});
grunt.loadNpmTasks("require-wild");
grunt.registerTask('default', ['requireWild']);
Then run the grunt task. Your file will be generated. Modify your setup to load namespaces.js
require(['namespaces'], function () { ... });
This now allows modules under src to use dependencies glob pattern matching.
require(['behaviors/**/*'], function (behaviors) { }
I know this is old, but I'd like to share my solution:
For this solution you need JQuery
1) Create a bash script that will list all the js files in
"MyDirectory/", and save it to "directoryContents.txt":
#!/bin/bash
#Find all the files in that directory...
for file in $( find MyDirectory/ -type f -name "*.js" )
do
fileClean=${file%.js} #Must remove .js from the end!
echo -n "$fileClean " >> MyDirectory/directoryContents.txt
done
File will look like this:
MyDirectory/FirstJavascriptFile MyDirectory/SecondJavascriptFile
MyDirectory/ThirdJavascriptFile
Problem with my script! Puts an extra " " at the end, that messes things up! Make sure to remove the excess space at the end of directoryContents.txt
2) Then in your Client side JS code:
do a "GET" request to retrieve the text file
For each entry (split by the space), 'require' that file:
.
$.get( "MyDirectory/directoryContents.txt", {}, function( data ) {
var allJsFilesInFolder = data.split(" ");
for(var a=0; a<allJsFilesInFolder.length; a++)
{
require([allJsFilesInFolder[a]], function(jsConfig)
{
//Done loading this one file
});
}
}, "text");
I was having a problem with this code not finishing before my other code, so Here's my extended answer:
define([''], function() {
return {
createTestMenu: function()
{
this.loadAllJSFiles(function(){
//Here ALL those files you need are loaded!
});
},
loadAllJSFiles: function(callback)
{
$.get( "MyDirectory/directoryContents.txt", {}, function( data ) {
var allJsFilesInFolder = data.split(" ");
var currentFileNum = 0;
for(var a=0; a<allJsFilesInFolder.length; a++)
{
require([allJsFilesInFolder[a]], function(jsConfig)
{
currentFileNum++;
//If it's the last file that needs to be loaded, run the callback.
if (currentFileNum==allJsFilesInFolder.length)
{
console.log("Done loading all configuration files.");
if (typeof callback != "undefined"){callback();}
}
});
}
}, "text");
}
}
});
What I ended up doing was everytime my Node server boots, it will run the bash script, populating directoryContents.txt. Then My client side just reads directoryContents.txt for the list of files, and requires each in that list.
Hope this helps!
There isn't really a way to do this conceptually on the fly (that I know of).
There's a few work arounds though:
Use grunt and concat and then just require that behemoth...I know, kinda sucky.
What I think is a better solution... use a require hierarchy like so:
require('/js/controllers/init', function(ctrls){
ctrls(app, globals);
});
// /js/controllers/init.js
define('js/controllers/index', 'js/controllers/posts', function(index, posts){
return function protagonist(app, globals){
var indexModule = index(app, globals);
var indexModule = posts(app, globals);
return app || someModule;
};
});
// /js/controllers/index.js
define('js/controllers/index', 'js/controllers/posts', function(index, posts){
return function protagonist(app, globals){
function method1(){}
function method2(){}
return {
m1: method1,
m2: method2
};
};
});
Note that "protagonist" function. That allows you to initialize modules before their use, so now you can pass in a 'sandbox' -- in this case app and globals.
Realistically, you wouldn't have /js/controllers/index.js... It should probably be something like /js/controllers/index/main.js or /js/controllers/index/init.js so that there is a directory adjacent to (sibling of) /js/controllers/init.js called "index". This will make your modules scalable to a given interface -- you can simply swap modules out and keep your interface the same.
Hope this helps! Happy coding!
I wrote a library to solve this problem. Eventually someone else came along and improved my library, here it is:
https://github.com/smartprocure/directory-metagen
You can use my lib with Gulp or whatever - it generates metadata for your project and RequireJS can use that metadata to require the desired files from the filesystem.
Using this lib will produce a RequireJS module that looks something like this:
define(
[
"text!app/templates/dashboardTemplate.ejs",
"text!app/templates/fluxCartTemplate.ejs",
"text!app/templates/footerTemplate.ejs",
"text!app/templates/getAllTemplate.ejs",
"text!app/templates/headerTemplate.ejs",
"text!app/templates/homeTemplate.ejs",
"text!app/templates/indexTemplate.ejs",
"text!app/templates/jobsTemplate.ejs",
"text!app/templates/loginTemplate.ejs",
"text!app/templates/overviewTemplate.ejs",
"text!app/templates/pictureTemplate.ejs",
"text!app/templates/portalTemplate.ejs",
"text!app/templates/registeredUsersTemplate.ejs",
"text!app/templates/userProfileTemplate.ejs"
],
function(){
return {
"templates/dashboardTemplate.ejs": arguments[0],
"templates/fluxCartTemplate.ejs": arguments[1],
"templates/footerTemplate.ejs": arguments[2],
"templates/getAllTemplate.ejs": arguments[3],
"templates/headerTemplate.ejs": arguments[4],
"templates/homeTemplate.ejs": arguments[5],
"templates/indexTemplate.ejs": arguments[6],
"templates/jobsTemplate.ejs": arguments[7],
"templates/loginTemplate.ejs": arguments[8],
"templates/overviewTemplate.ejs": arguments[9],
"templates/pictureTemplate.ejs": arguments[10],
"templates/portalTemplate.ejs": arguments[11],
"templates/registeredUsersTemplate.ejs": arguments[12],
"templates/userProfileTemplate.ejs": arguments[13]
}
});
You can then require modules in your front-end like so:
var footerView = require("app/js/jsx/standardViews/footerView");
however, as you can see this is too verbose, so the magic way is like so:
name the dependency above as allViews!
now you can do:
var allViews = require('allViews');
var footerView = allViews['standardViews/footerView'];
There are two advantages to requiring directories whole:
(1) in production, with the r.js optimizer, you can point to one dependency (module A) and it can then easily trace all of A's dependencies that represent a entire directory
(2) in development, you can require whole directories up front and then use synchronous syntax to require dependencies because you know they have already been loaded
enjoy "RequireJS-Metagen"
https://github.com/smartprocure/directory-metagen
https://www.npmjs.com/package/requirejs-metagen
https://github.com/ORESoftware/requirejs-metagen

Categories

Resources