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.
Related
I am developing an AngularJS application. To ship the code in production, I'm using this Grunt configuration/task:
grunt.registerTask( 'compile', [
'sass:compile', 'copy:compile_assets', 'ngAnnotate', 'concat:compile_js', 'uglify', 'index:compile'
]);
It's really hard to debug, and it's kind of a question to people who already ran into such problems and can point to some direction.
My main module is including those submodules:
angular
.module('controlcenter', [
'ui.router',
'ui.bootstrap',
'templates-app',
'templates-common',
'authentication',
'api',
'reports',
'interceptors',
'controlcenter.websites',
'controlcenter.users',
'controlcenter.campaigns',
'controlcenter.reports',
'controlcenter.login'
])
.run(run);
The error I get is following:
Uncaught Error: [$injector:modulerr] Failed to instantiate module controlcenter due to:
Error: [$injector:modulerr] Failed to instantiate module controlcenter.websites due to:
Error: State 'websites'' is already defined
If I remove the websites module, I get the same error for
controlcenter.users.
I am using the ui-router to handle routing inside the app.
After my build process (for integration testing), everything works just fine:
grunt.registerTask( 'build', [
'clean', 'html2js', 'jshint', 'sass:build',
'concat:build_css', 'copy:build_app_assets', 'copy:build_vendor_assets',
'copy:build_appjs', 'copy:build_vendorjs', 'copy:build_vendorcss', 'index:build', 'karmaconfig',
'karma:continuous'
]);
So maybe ngAnnotate or or concat/uglify are doing weird things here?
UPDATE 1:
It has something to do with my configuration of the modules. Here is the code:
angular
.module('controlcenter.websites',
[
'ui.router'
]
)
.config(config);
config.$inject = ['$stateProvider'];
function config($stateProvider) {
$stateProvider.state( 'websites', {
url: '/websites',
views: {
"main": {
controller: 'WebsitesController',
templateUrl: 'websites/websites.tpl.html'
}
}
});
}
When I change the name of the state to websites_2, I get an error
with 'websites_2 is already defined'.
When I remove the module completely, the next one hast the same problem inside the config file. So is the structure wrong?
Update 2:
The problem seems concat related.
It takes every JS file and adds it one after another to one, bigger file. All of my modules are at the end. The last module always has the problem with 'state already defined'. So it's not just the order of the modules appending to each other, it's something elsse...
Update 3:
I placed my code (I've excluded every Controller-Code and functions, just the scaffold) in a gist. This is the outcome after my compile process, without uglifying it.
Issue:
You have multiple files that contains a config function to configure your module, like this:
angular
.module('controlcenter.websites', [])
.config(config);
function config() {
// ...
}
The problem is that after you concatenate all files you end up with a big file with multiple declarations of config. Because of JavaScript's variable hoisting, all declarations are moved to the top and only the very last of them is evaluated, and this one is:
function config($stateProvider) {
$stateProvider.state( 'websites', {
url: '/websites',
views: {
"main": {
controller: 'WebsitesController',
templateUrl: 'websites/overview/websites.tpl.html'
}
},
data : {requiresLogin : true }
});
}
Hence, each time you .config(config) a module, you are telling Angular to configure your module with that particular configuration function, which means that it executes multiple times and tries to define the state websites more than once.
Solution:
Wrap each JavaScript file code with a closure. This way you will avoid declaring a variable/function more than once:
(function (angular) {
'use strict';
angular
.module('controlcenter.website.details', ['ui.router'])
.config(config);
config.$inject = ['$stateProvider'];
function config($stateProvider) {
$stateProvider
.state( 'websiteDetails', {
url: '/websiteDetails/:id',
views: {
"main": {
controller: 'WebsiteDetailsController',
templateUrl: 'websites/details/website.details.tpl.html'
}
},
data : {requiresLogin : true }
})
.state( 'websiteDetails.categories', {
url: '/categories',
views: {
"detailsContent": {
templateUrl: 'websites/details/website.details.categories.tpl.html'
}
},
data : {requiresLogin : true }
})
;
}
})(window.angular);
Edit:
I strongly recommend you wrap your files into closures. However, if you still don't want to do that, you can name your functions according to their respective modules. This way your configuration function for controlcenter.website.details would become controlcenterWebsiteDetailsConfig. Another option is to wrap your code during build phase with grunt-wrap.
window.angular and closures: This is a technique I like to use on my code when I'm going to uglify it. By wrapping your code into a closure and giving it a parameter called angular with the actual value of window.angular you are actually creating a variable that can be uglified. This code, for instance:
(function (angular) {
// You could also declare a variable, instead of a closure parameter:
// var angular = window.angular;
angular.module('app', ['controllers']);
angular.module('controllers', []);
// ...
})(window.angular);
Could be easily uglified to this (notice that every reference to angular is replaced by a):
!function(a){a.module("app",["controllers"]),a.module("controllers",[])}(window.angular);
On the other side, an unwrapped code snippet like this:
angular.module('app', ['controllers']);
angular.module('controllers', []);
Would become:
angular.module("app",["controllers"]),angular.module("controllers",[]);
For more on closures, check this post and this post.
If you check it in the concatenated file, do you have the states defined twice? Can it be that you are copying the files twice? Check the temporary folders from where you are taking the files (also in grunt config, what you are copying and what you are deleting...).
So I had the same problem but with the following setup:
yeoman angular-fullstack (using typescript)
Webstorm
With the angular-fullstack configuration, the closures were already implemented (as Danilo Valente suggests) so I struggled quite a bit until I found out that in Webstorm, I had the typescript compiler enabled which compiled all of my *.ts files to *.js. But since Webstorm is so 'smart', it does not show these compiled files in the working tree. Grunt however concatenated of course all files regardless if it is typescript of JS. That's why - in the end- all of my states were defined twice.
So the obvious fix: Disabled typescript compiler of webstorm and deleted all the generated *.js files and it works.
I have src of my application. I use AngularJS. I use RequireJS as module loader. I use Grunt as task runner. When I run application using src: everything is good. When I build application with Grunt, application is not working. I got no errors in console.
Main thing I noticed: my code (code of my application: app.js and files under js/) does not appear in output file which is set in grunt task settings. Also, I don't think there is something about AngularJS.
Main config file:
require.config({
paths: {
'angular' : '../components/angular/angular',
/* etc..... */
'jquery': '../components/jquery/dist/jquery',
'application': './app'
},
shim: {
/* etc */
application: {
deps: ['angular']
},
angular: {
exports : 'angular'
}
},
baseUrl: '/js'
});
require(['application', 'angular', 'ngRoute', 'bootstrap' /* ngRoute and bootstrap from etc :) */], function (app) {
app.init();
});
My app in app.js is:
define([
'require', 'angular', 'main/main', 'common/common'
], function (require) {
'use strict';
var angular = require('angular');
var app = angular.module('myApp', ['ngRoute', 'main', 'common']);
app.init = function () {
angular.bootstrap(document, ['myApp']);
};
app.config(['$routeProvider',
function ($routeProvider) {
$routeProvider
./* ... some code */
}
]);
return app;
});
I add main RequireJS config file at the end of body tag:
<script type="text/javascript" src="components/requirejs/require.js" data-main="js/bootstrap.js"></script>
Now I have problem. I have Grunt as build system. I have this task:
grunt.initConfig({
requirejs: {
compile: {
options: {
baseUrl: "public/js",
mainConfigFile: "public/js/bootstrap.js",
name: 'bootstrap',
out: "build/js/bootstrap.js",
optimize: 'none'
}
}
},
// etc
I have no optimisation, so I get ~11k lines of code in output file.
As I said. Main problem is: no AngularJS code and no application code in output file.
Why? I set up mainConfigFile correctly. Problem is in RequireJS config file? But everything is okay, when I am running my app on src.
It would be better if you can provide the exactly error output you get. And where you got it (from browser's console or from terminal during build process)
For now I will suggest some adjustments what could possibly help with your case.
angular: {
exports : 'angular'
}
Here you have already export angular.js into global local variable (inside every require and define block).
And by doing var angular = require('angular'); you are possibly asynchronously override angular variable inside your app.js module.
For 'require' being added into define block, as r.js always reading what module got to be loaded in very first step, and then merged into single file. And this may confuse r.js to merging requireJS into itself.
Suggest this adjustment for your app.js:
define([ // Removed 'require' because no needed , it is already global and usable anywhere
'angular', 'main/main', 'common/common'
], function () {
'use strict';
// var angular = require('angular'); // This is a very common mistake. You are not going to call angular this way, requireJS difference with commonJS.
var app = angular.module('myApp', ['ngRoute', 'main', 'common']);
app.init = function () {
angular.bootstrap(document, ['myApp']);
};
app.config(['$routeProvider',
function ($routeProvider) {
$routeProvider
./* ... some code */
}
]);
return app;
});
And last but not least data-main="js/bootstrap.js" I think it should be js/main.js or a typo.
EDIT added explanations for 'require' in define block, and angular local variable.
My setup so far:
<script src="/common/js/require.configure.js"></script>
<script src="/common/js/lib/require.js" data-main="/common/js/app.js"></script>
require.configure.js
var require = {
baseUrl: '/',
paths: {
"jquery": "/common/js/lib/jquery",
"fastclick": "/common/js/lib/fastclick",
"knockout": "/common/js/lib/knockout",
"common": "/common/js/scripts/common"
}
};
The top three paths are obviously just libraries that I am using for my application. The last file "common" is a collection of functions that are global to my application such as opening the main menu, giving messages to the user, or binding handlers, etc.
app.js
define(["jquery", "knockout", "fastclick", "common"], function (){
});
I know that requireJS always needs a data-main file to run initially. But what does this above code really do? I trying to follow tutorials online but it is not helping. I'm guessing that by defining those strings in the array, it looks it up in the configuration file and is loading in those files, but how are those files then accessed or used? I'm guessing that I can simply then just "require" those same strings and they will be available to me in my functions?
common.js (simplified for Stack Overflow)
require(["knockout"], function (ko) {
var appViewModel = {};
appViewModel.loaded = ko.observable(false);
});
By wrapping everything in the require() I think that this is injecting the dependencies of needing knockout.
App's First Page - login.html (simplified for S.O.)
In the first page of the app, I define a <script> tag with the following
require(["jquery", "knockout", "fastclick", "common"], function ($, ko, FastClick)
{
$(function(){
appViewModel.loginData = {
email : ko.observable(),
password : ko.observable()
};
});
});
And the resulting error when trying to run is that
Uncaught ReferenceError: appViewModel is not defined
despite the fact that I have included "common" in the require([]).
What am I missing here? I think that I may be completely misunderstanding what "require" and "define" do in requireJS, so that would be a good basis of an answer for me.
i think you want to do something like that:
Modules that define global obj
require(["knockout"], function (ko) {
window.appViewModel = {};
window.appViewModel.loaded = ko.observable(false);
});
Modulw that popule the obj:
require(["jquery", "knockout", "fastclick", "common"], function ($, ko, FastClick)
{
window.appViewModel.loginData = {
email : ko.observable(),
password : ko.observable()
});
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
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.