There is something really weird going on when I'm using RequireJS with AngularJS. I managed to load all my angular dependencies through RequireJS. I can see those scripts downloaded when I open up the Sources pane in Chrome's developer tool. But Angular keeps throwing an error in the console that it failed to instantiate the module:
Uncaught Error: [$injector:modulerr] Failed to instantiate module MyTestApp due to:
Error: [$injector:nomod] Module 'MyTestApp' is not available! You either misspelled the module name or forgot to load it. If registering a module ensure...<omitted>...0)
It seems like Angular, when loaded with RequireJS, cannot bind with the ng-app tag in the HTML page. I'm not sure if this is the case but it seems like so to me because when I import angular.min.js manually into the HTML page, it all works fine.
Did I do anything wrong when using RequireJS with Angular? How should I use the two together? Here's how my code look:
index.html
<!doctype html>
<html lang="en" ng-app="MyTestApp">
<head>
<meta charset="utf-8">
<title>AngularJS</title>
<link rel="stylesheet" href="css/style.css"/>
<script data-main="main" src="js/require.js"></script>
</head>
<body>
<div ng-controller="TestController">{{helloMessage}}</div>
</body>
</html>
main.js
require.config({
baseUrl: "scripts/app",
shim: {
"angular": {
exports: "angular"
},
"angular.route": {
deps: ["angular"]
},
"bootstrapper": {
deps: ["angular", "angular.route"]
},
},
paths: {
"angular": "../angular",
"angular.route": "../angular-route",
"bootstrapper": "bootstrapper"
}
});
require(["angular", "angular.route", "bootstrapper"],
(angular, ngRoute, bootstrapper) => {
bootstrapper.start();
}
);
bootstrapper.js
function run() {
app = angular.module("MyTestApp", ["ngRoute"]);
app.controller("TestController", TestController);
console.log(app); //Prints object to console correctly, ie, angular was loaded.
}
Here is how I would do it (DEMO).
In main.js, require angular, your app and maybe a controllers.js and other files:
require(['angular', 'app'], function (app) {
angular.element(document).ready(function () {
angular.bootstrap(document, ['MyTestApp']);
});
});
In app.js, require angular and angular route:
define(['angular', 'angular.route'], function() {
var app = angular.module("MyTestApp", ["ngRoute"]);
return app;
});
This is manual bootstraping and therefore does not need the ng-app tag at all.
I'm currently working on a pretty big application with angular and requirejs and I prefer to load the "big" libraries that are used by the whole app anyway independently from requirejs.
So I load one big file which includes angular, angular-route, requirejs, main.js in the beginning. Or if it makes sense to use a CDN version, load it from there. On the other hand I load every controller, directive, filter and service on request. I currently have 50+ controllers which allready makes a difference in initial load time.
But that all depends on the size of your app.
First you does not need to get a variable from the load of "angular.route". The module will be directly loaded in angular.
I think you should also wait for the dom ready event and also make a requirejs app module that will be in charge of loading all app dependencies:
app/app.js:
define([
"angular",
"angular-route",
"app/controllers",
"app/directives",
[...]
], function(angular){
var app = angular.module('app', [
"ngRoute",
"app.controllers",
"app.directives",
[...]
])
.config([function(){
// app configuration goes here
}]);
return app;
})
main.js
require(["angular", "app/app"],
function (angular, app){
angular.element(document).ready(function() {
angular.bootstrap(document, [app.name]);
});
}
);
Related
I'm writing an Angular application using Webpack.
I have a user profile JavaScript object that I'd like to template into the index page via an inline script, for performance, so that the client doesn't have make another request and delay the rest of the page loading.
Using requirejs you can name an inlined module so you can depend on it later. Is there any way of doing this webpack? or am I stuck with declaring it as a global?
As an example here's what you could do in Require JS;
<html>
<body>
...
<script>
define('userProfile', [
'angular'
], function(angular) {
return angular.module('userProfile', [])
.constant('userProfile', Object.freeze({
id: '$!{user.userid}',
name: '$!{user.fullname}',
userType: '$!{user.userType}'
}))
});
</script>
...
</body>
</html>
The user fields such as '$!{user.userid}' are templated in when the index page is served.
To depend on this in your app you could simply do something like
define([
'userProfile',
], function() {
return angular.module('my-app', [
'userProfile',
]);
});
You can specify a variable or module to be external on your webpack config so that you just include it on the page by a script tag. This is what i do to skip libraries bundle with my script
// webpack.config.js
"externals": {
"jquery": "jQuery",
"angular": "angular"
},
But as angular works you don't need to import the module with webpack
<script>
(function(angular) {
angular.module('userProfile', [])
.constant('userProfile', Object.freeze({
id: '$!{user.userid}',
name: '$!{user.fullname}',
userType: '$!{user.userType}'
}))
})(angular);
</script>
And in your module you can.
angular.module('whatever', ['userProfile'])
Now you can use the user profile constant anywhere in your angular application.
angular.module('whatever').controller(['userProfile', function (userProfile) {}]);
You just need to make sure that the userProfile script is executed before the your bundle
fter including require tag the application is behaving abnormal way .is there any way i can bootstrap my application apart from below code .
main.js
require(['/module.js'], function() {
angular.element(document).ready(function () {
angular.bootstrap(document, ['myApp']);
});
});
When I written as single file js file the code is working properly.
module.js
var name = 'myApp';
angular.module(name, [])
.controller('Controller', require(['controller.js']))
.factory('Service', require(['service.js']))
.filter('Number', require(['filter.js']));
I have included my main.js in index.html . index html has 3 views i am displaying them based on ng-show from index.html.
The problem is module.js loading properly and js files too. Script is not executing properly so that my entire index.html page including 3 views displayed automatically with error messages.
Control is not going to controller.js/service.js
Error :
Error: Unknown provider: depsProvider <- deps .
Did i miss any define code? Thanks in advance
Angular does not support AMD by default, You need to config angular to export angular object. Please check out the this post for more details.
require.config({
paths: {
'angular': '../lib/angular/angular'
},
shim: {
'angular': {
exports: 'angular'
}
}
});
Your module.js should be defined with define method of requirejs and it should return module.
You can omit file extesion (.js) while using requireJs
I am experimenting and trying to use the angular.element (jQuery wrapper) inside angular's config() function. However, in the main.js file, as soon as I add the deps under angular, it generates the $injector:modulerr error (where the 1st comment is)
Script tag is placed right before in my html file
<script src="js/vendor/require.min.js" data-main="js/own/main.js"></script>
main.js file
require.config({
baseUrl: "js/vendor/",
paths: {
"jquery": "jquery-2.1.1.min",
"angular": "angular.1.2.9.min"
},
shim: {
"angular": {
deps: ["jquery"], // As soon as I add this, it generates a $injector:modulerr error
exports: "angular"
}
}
});
require(["angular"], function(angular){
angular
.module("app", []) // ng-app="app" already defined in <body>
.controller("appctrl", function($scope){ // ng-controller="appctrl" already defined in <body>
$scope.sample = 1; // this works fine if I don't add deps under angular in require.config
});
});
Error is gone by removing the directive "ng-app='urAppName'" in the html page. The ng-app has to be manually bootstrapped by angular.bootstrap(document.body, ["urAppName"]) AFTER you define your angular module, e.g. angular.module("urAppName", []).controller().....;
Note that you can leave other directives such as ng-controller or your model data such as {{modelName}} in the page.
Trying to create an AMD Javascript library to be included in non-AMD projects. Here's my setup:
app.coffee
define ->
class App
constructor: -> console.log 'instantiating App'
init: -> console.log 'Init called'
index.html
<body>
<script type="text/javascript" src="//code.jquery.com/jquery-1.10.2.min.js"></script>
<script type="text/javascript" src="dev-latest.js"></script>
<script type="text/javascript">
$(document).ready(function(){
console.log('doc', window.app);
});
$(function(){
console.log('func', window.app);
});
window.onload = function()
{
console.log('onload', window.app);
}
</script></body>
main.js
require(['cs!app'], function(app){
return window.app = new app;
});
I am building this project with r.js optimizer to get dev-latest.js as the output. Here's my build file (PS: Build is successful):
({
baseUrl: './vendor',
paths: {
app: '../app',
'require-lib': 'require'
},
name: '../main',
out: 'dev-latest.js',
include: 'require-lib',
preserveLicenseComments: false
})
When running the code in the browser here's the output:
doc undefined
func undefined
onload undefined
instantiating App dev-latest.js:1
app.init(); // running this manually in the browser console
Init called
How should I go about this and get app to load before being used ?
Using AMD you cant rely on a module being created outside of a module. The only reliable way is to load the result of a module into another module. So in your case need to create a new module which then can lsiten to $.read:
define( [App], (App)->
$(document).ready(function(){
console.log('doc', App);
});
)
Solved it by using browserify (just found about it) which uses the CommonJS form of dependency loading and saves all that clutter. Also a great template for starting projects is amitayd's grunt-browserify-jasmine setup
In my project I want to use RequireJS and bootstrap my app as follows:
requirejs.config({
baseUrl: 'scripts/vendor',
paths: {
jquery: [
'https://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min',
'jquery'
],
angular: [
'http://ajax.googleapis.com/ajax/libs/angularjs/1.0.4/angular.min',
'angular'
],
app: '../app'
}
});
require(['require', 'jquery', 'angular', 'app'], function(require, $, angular, app) {
console.log(require);
console.log($);
console.log(angular);
console.log(app);
});
On my index.html only RequireJS is loaded via script tag, where the RequireJS loads the above code.
What works:
- in my Network monitor I can see that RequireJS, jQuery, Angular and app are loaded
- The console.log messages print correct for require, jQuery and app
The angular object is somehow undefined. But if I don't load it from CDN and use my local load, it works! The local file is a RequireJS wrapper that looks like this:
define(['/components/angular/angular.min.js'], function () {
return angular;
});
How do I get this work with Angular'S CDN? Or does this depend on support from Angular?
First, you are confusing "paths" with "shim"
Path is good, don't go for "shim" behavior. But, you need to make your "paths" proper:
paths: {
jquery: 'https://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min',
// NOTE: angular is "plain JS" file
angular: 'http://ajax.googleapis.com/ajax/libs/angularjs/1.0.4/angular.min',
app: '../app'
}
Then, you need to let go of the need to have something returned to you... Just "use the force, Luke" :) and expect the right globals to be there when you need them:
require(['jquery', 'app', 'angular'], function($, app, thisValueDoesNotMatter) {
// you don't need to wrap "require" Just use global
console.log(require);
console.log($);
console.log(app);
// note, angular is loaded as "plain JavaScript" - not an AMD module.
// it's ok. It returns "undefined" but we just don't care about its return value
// just use global version of angular, which will be loaded by this time.
// because you mentioned it in your dependencies list.
console.log(window.angular);
});