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.
Related
As we know, when use RequireJS in the configuration file we must define 'paths' and 'shim'. When writing tests (Karma, Jasmine), we need to create an additional configuration file for RequireJS and re-define the same data.
I try to extract common parts and load them dynamically. In Angular JS application, everything works without problems, but in test i have always error '404'. But let's get to the beginning. Simple example structure:
.
|-- newApp
| |-- app
| | `-- app.require.js
| |-- newApp.require.js
| `-- index.html
`-- test
|-- test.karma.js
`-- test.require.js
index.html load RequireJS config file
<script data-main="newApp.require.js" src="bower_components/requirejs/require.js"></script>
newApp.require.js init RequireJS
require([
'app/app.require'
], function (appRequire) {
"use strict";
require.config({
baseUrl: 'app',
paths: appRequire.paths,
shim: appRequire.shim,
deps: [
'NewAppBootstrap'
]
});
});
app.require.js Module / object with paths and shim
define([
'some/others/components.require'
], function (componentsRequire) {
"use strict";
var appRequire = {
'NewApp': 'app.module',
'NewAppBootstrap': 'app.bootstrap',
'NewAppRoute': 'app.routes'
};
var vendorRequire = {
'jQuery': {
exports: '$'
},
'angular': {
exports: 'angular'
}
};
return {
paths: Object.assign(
appRequire,
componentsRequire
),
shim: vendorRequire
};
});
Up to this point everything works fine. Now I would like to file app.require.js load into test.require.js. And the problems begin...
test.require.js
var TEST_REGEXP = /(spec|test)\.js$/i;
var allTestFiles = [];
Object.keys(window.__karma__.files).forEach(function (file) {
'use strict';
if (TEST_REGEXP.test(file)) {
var normalizedTestModule = file.replace(/^\/base\/|\.js$/g, '');
allTestFiles.push(normalizedTestModule);
}
});
require([
'app/app.require'
], function (appRequire) {
"use strict";
require.config({
baseUrl: '/base/newApp',
waitSeconds: 200,
paths: appRequire.paths,
shim: appRequire.shim,
deps: allTestFiles,
callback: window.__karma__.start
});
});
Unfortunately, I still have error:
WARN [web-server]: 404: /app/app.require.js
PhantomJS 2.1.1 (Linux 0.0.0) ERROR: 'There is no timestamp for app/app.require.js!'
PhantomJS 2.1.1 (Linux 0.0.0) ERROR
Error: Script error for "app/app.require"
http://requirejs.org/docs/errors.html#scripterror at node_modules/requirejs/require.js:143
I tried different paths, but still nothing. Does anyone know how to load this file? Whether it is at all possible load file in this place? I would be grateful for any tips.
By the time your code hits this require call:
require([
'app/app.require'
],
There is no RequireJS configuration in effect, and data-main is not used, so by default RequireJS takes the directory that contains the HTML page that loads RequireJS as the baseUrl (See the documentation.) Since index.html sits at the root, then RequireJS resolves your module name to /app/app.require.js and does not find it.
You can work around it by using a full path:
require([
'base/newApp/app/app.require'
],
Or if it so happens that other code later is going to try to access this same module as app/app.require, then you should have a minimal configuration before your first require call:
require.config({
baseUrl: '/base/newApp',
});
It is perfectly fine to call require.config multiple times. Subsequent calls will override values that are atomic (like baseUrl) and marge values that can be merged. (An example of the latter would be paths. If the first call sets a paths for the module foo and a 2nd call sets a paths for the module bar, then the resulting configuration will have paths for both foo and bar.)
I'm trying to unit test an AngularJS/Browserify application via karma-browserify. Ultimately, when I run my gulp karma task, I get the error Error: [$injector:nomod] Module 'myApp' is not available! You either misspelled the module name or forgot to load it...
My gulpfile.js has the task
gulp.task('test', function(done) {
new karma.Server({
configFile: __dirname + '/karma.conf.js'
}, done).start();
});
My karma.conf.js includes
{
// ...
frameworks: ['browserify', 'jasmine'],
files: [
'node_modules/angular/angular.js',
'node_modules/angular-mocks/angular-mocks.js',
'spec/**/*.js'
],
preprocessors: {
'spec/**/*.js': [ 'browserify' ]
},
browserify: {
debug: true
}
// ...
}
I define my module in a main.js that includes
require('angular').module('myApp', [
//...lots of `require`s for each dependency...
]);
I define my controller in a MainCtrl.js that looks like
function MainCtrl(/*...deps...*/) {
var ctrl = this;
ctrl.foo = 'bar';
}
module.exports = MainCtrl;
then register the controller elsewhere like
var app = require('angular').module('myApp');
app.controller('MainCtrl', [/*...deps...*/, require('./MainCtrl')]);
Finally my test looks like
(function() {
"use strict";
describe('MainCtrl', function() {
var ctrl;
beforeEach( angular.mock.module('myApp') );
beforeEach( inject( function($controller) {
ctrl = $controller('MainCtrl');
}));
it('should be defined', function() {
expect(ctrl).toBeDefined();
});
});
}());
Workaround
The workaround I have is to add my main.js file to karma.conf.js
files: [
'js/main.js', // ADDED: Defines the app and `require`s angular
'node_modules/angular-mocks/angular-mocks.js',
'spec/**/*.js'
],
preprocessors: {
'js/main.js': ['browserify'], // ADDED
'spec/**/*.js': ['browserify']
}
and everything works. But I thought I wasn't supposed to add my source files to karma (at least not with karma-browserify). Is this the right way to set up my project?
Yes, the 'workaround' is the desired way to use karma-browserify.
preprocessors definition specifies which of the included files should be processed by which preprocessor but does not include them:
The keys of the preprocessors config object are used to filter the
files specified in the files configuration.
it is files definition that actually includes the files:
The files array determines which files are included in the browser and
which files are watched and served by Karma.
Files tells Karma which files it should load relative to the base path. These are:
All test related libraries
Our source code to test
The tests themselves
I'm using Webjars to import AngularJS into my web project.
For some reason the minified version of AngularJS won't be served even though I'm referencing those in my main. I was expecting to see angular.min.js and angular-route.min.js being loaded, but I'm seeing the regular angular.js and angular-route.js. What am I doing wrong here?
My main.js:
'use strict';
requirejs.config({
paths: {
'angular': '../lib/angularjs/angular.min',
'angular-route': '../lib/angularjs/angular-route.min',
'async': '../lib/requirejs-plugins/src/async'
},
shim: {
'angular': {
exports : 'angular'
},
'angular-route': {
deps: ['angular'],
exports : 'angular'
}
}
});
require(['angular', './controllers', './directives', './filters', './services', 'angular-route','./places-autocomplete','async','./gmaps'],
function(angular, controllers) {
initialize();
// Declare app level module which depends on filters, and services
angular.module('myApp', ['myApp.filters', 'myApp.services', 'myApp.directives', 'ngRoute']).
config(['$routeProvider', function($routeProvider) {
....
}]);
angular.bootstrap(document, ['myApp']);
});
My html loads requirejs like this:
<script>
#Html(org.webjars.RequireJS.getSetupJavaScript(routes.WebJarAssets.at("").url))
</script>
<script data-main="#routes.Assets.versioned("javascripts/main.js")"
src="#routes.WebJarAssets.at(WebJarAssets.locate("require.min.js"))"></script>
and the above requirejs.config snippet resides in main.js
I looked into the sources of requirejs. Here's what I found:
requirejs splits each path, you defined in the config object, into its components (i.e. the directories, the filename and the extension). For some reason (node module naming conventions) the last extension is dropped. They do not check if that's a '.js'. Then this path array is used to access a module. Without a plugin rquirejs only handles .js files. It adds a .js if nessecary.
Now you can see what happens in your example. In the first step requirejs drops the .min extension. When it loads the module it joins the path components and adds a .js to the end. Then it loads the full module and not the minified version.
If you add a .js to your paths, then this .js was dropped and the .min is still there.
Try to add
enforceDefine: true,
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.
I'm trying to inject a third party file uploader into my Angular App. I had it working fine but took a break from the project and revisiting it now I've decided to use requireJS, and am having a terrible time trying to inject this module in. I'm sure it's just semantics as to how to get it to actually work but I'm not exactly sure what I am doing wrong.
File: main.js
Loading the uploader here and giving angular as the dependancy.
require.config({
paths: {
angular: '../bower_components/angular/angular.min',
angularRoute: '../bower_components/angular-route/angular-route.min',
angularFileUpload: 'modules/file-upload/angular-file-upload',
jquery: '//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min',
fastclick: '//cdnjs.cloudflare.com/ajax/libs/fastclick/1.0.0/fastclick.min'
},
shim: {
'angular' : {'exports' : 'angular'},
'angularRoute': ['angular'],
'angularFileUpload': ['angular'],
'jquery': {
exports: '$'
}
},
priority: [
"angular"
]
});
// http://code.angularjs.org/1.2.1/docs/guide/bootstrap#overview_deferred-bootstrap
window.name = "NG_DEFER_BOOTSTRAP!";
require( [
'angular',
'routes',
'jquery',
'fastclick',
'app'
], function(angular, app) {
'use strict';
// Some config stuff here
});
File: app.js
This is where all of the angular modules are loaded into the app, This is mainly where I have been trying to inject the file uploader, in a similar fashion.
define([
'angular',
'filters',
'services',
'directives',
'controllers',
'angularRoute'
], function (angular, filters, services, directives, controllers) {
'use strict';
// Declare app level module which depends on filters, and services
return angular.module('myApp', [
'ngRoute',
'myApp.controllers',
'myApp.filters',
'myApp.services',
'myApp.directives'
]);
});
File: angular-file-upload.js
The module, It looks like it's all setup to fit in with the AMD structure.. here is the top part of the file, the rest of the file is directives and factories like angularFileUpload.directive()
(function(angular, factory) {
if (typeof define === 'function' && define.amd) {
define('angular-file-upload', ['angular'], function(angular) {
return factory(angular);
});
} else {
return factory(angular);
}
}(angular || null, function(angular) {
/**
* The angular file upload module
* #author: nerv
* #version: 0.2.9.6, 2013-12-06
*/
var angularFileUpload = angular.module('angularFileUpload', []);
If anybody could provide any information on how this could be injected I would greatly appreciate it!
Basically, you first need to define the path to the module in your requirejs config:
require.config({
paths: {
"angular" : "relative/path/to/angular",
"angular-file-upload" : "relative/path/to/angular-file-upload",
... other modules
},
shim: {
"angular": {
exports: "angular"
},
// Currently needed (see explanation below)
"angular-file-upload": ["angular"]
}
})
As you can see, I needed to define a shim as well - which should not have been needed since angular-file-upload already is AMD friendly and defines that it relies on angular. That is what you see here:
define('angular-file-upload', ['angular'], function(angular) {
return factory(angular);
});
However, due to what I see as a bug (and which I have described here https://github.com/nervgh/angular-file-upload/issues/171) the angular-file-upload script checks for the existence of angular when loaded - but since angular is not a global variable yet, that fails with an exception (Uncaught ReferenceError: angular is not defined angular-file-upload.js:13) causing the library not to make itself available as a requirejs module.
Anyway, with the shim fix applied, that would not be an issue for now.
Then, before we are all set, in your app.js file - you need to set angular-file-upload as a module dependency and make sure to register the angular module angularFileUpload with you angular app module - and then you should be ready to rock. So something like this should work:
define([
'angular',
'filters',
'services',
'directives',
'controllers',
'angularRoute',
'angular-file-upload'
], function (angular, filters, services, directives, controllers) {
'use strict';
// Declare app level module which depends on filters, and services
return angular.module('myApp', [
'ngRoute',
'myApp.controllers',
'myApp.filters',
'myApp.services',
'myApp.directives',
'angularFileUpload'
]);
});