How to resolve dependencies for a project using AngularJS and RequireJS? - javascript

I have a simple AngularJS application which I am trying to refactor to use RequireJS.
Since controllers and services are loaded async, I can't use ng-app in my index.html.
Following is my main.js
require.config({
paths: {
"angular": '../../bower_components/angular/angular',
"angularCookies": '../../bower_components/angular-cookies/angular-cookies'
},
shim: {
angular: {
exports: "angular"
},
angularCookies : {
deps: ["angular"]
}
}
});
require(['angular', './login/js/login'],
function (angular) {
angular.bootstrap(document, ['loginApp']);
}
);
My login.js is where I am defining an angular module.
Following is my login.js
'use strict';
define(['angular',
'angularCookies',
'./login.controller'],
function(angular, angularCookies, loginController) {
angular.module('loginApp', [
'ngCookies'])
.config(['$cookiesProvider',
function($cookiesProvider) {
$cookiesProvider.defaults.path = '/';
$cookiesProvider.defaults.domain = 'localhost';
}
])
.run(['$cookies',
'loginService',
function($cookies, loginService) {
}
]).controller(loginController);
});
As seen, it is dependent on loginController and loginController is dependent on loginService.
My loginService is defined as --
"use strict";
define(['angular',
'angularCookies'],
function (angular, angularCookies) {
var loginService = angular.module('loginApp')
.factory('loginService', [
'$http',
'$cookies',
function ($http, $cookies) {
// My functions and other code here.
}]);
return loginService;
});
With this configuration I am getting an error -
Module 'loginApp' is not available! You either misspelled the module name or forgot to load it. If registering a module ensure that you specify the dependencies as the second argument.
What am I missing here?
What configuration do I need to do to make it right?

I see a couple of problems. First the app shouldn't be created inside login. The app is the base of all controllers and services.
So I would move the app creation to another file called app.js.
Then in my require config:
shim: {
'app': {
deps: ['angular', 'angular-route', 'angularCookies']
},
angularCookies : {
deps: ["angular"]
}
}
And:
require
(
[
'app'
],
function(app)
{
angular.bootstrap(document, ['loginApp']);
}
);
And then your controller would be:
define(['loginApp'], function(app)
{
app.controller('loginController',
[
'$scope',
function($scope)
{
//...
}
]);
});

Related

angular.js:15697 Error: [$injector:unpr] Unknown provider

I am trying to use requireJs and AngularJs together.
when I want to inject some factories I get the following error:
angular.js:15697 Error: [$injector:unpr] Unknown provider: StudentQueriesProvider <- StudentQueries <- schoolCtrl
search.js
define([
'angular',
],function (angular) {
angular.module('app',[])
.controller('schoolCtrl',['$scope','StudentQueries'
,function ($scope,studentQueries) {
$scope.students=studentQueries.queryStudentByName('name');
}]);
});
studentQueries.js
'use strict'
define([
'angular'
], function (angular) {
debugger;
(function (angular) {
angular.module('studentQueries', ['graphQl'])
.factory('studentQueries', ['graphQl'], function (graphQl) {
let queryStudentByName = function (firstName) {
//some logics
};
return {
queryStudentByName: queryStudentByName
};
}
);
})(angular)
}
)
graph.js
'use strict';
define([
'angular'
], function (angular) {
(function (angular) {
var service = angular.module('graphQl', []);
service.factory('graphQl', ['$http', function ($http) {
const sendQuery=(query,variables)=>{
//some logics
};
return {
sendQuery:sendQuery
};
}])
})(angular)
})
and finally my require-config.js
require.config({
paths: {
angular: 'bower_components/angular/angular',
jquery:'bower_components/jquery/dist/jquery'
},
shim: {
"angular": {
deps: [
'jquery'
],
exports: "angular"
}
}
});
require([
'angular',
'scripts/controllers/student/search',
'scripts/services/students/studentQueries',
'scripts/services/graph'
],function () {
angular.bootstrap(document, ['app']);
}
);
You are trying to inject 'StudentQueries' while defining the factory as 'studentQueries' , as Raimond comment out seems those injectors and definitions are all case sensitive.
Edited search.js
define([
'angular',
],function (angular) {
angular.module('app',[])
.controller('schoolCtrl',['$scope','studentQueries'//changed casing here
,function ($scope,studentQueries) {
$scope.students=studentQueries.queryStudentByName('name');
}]);
});
I needed to add app.js and inject it into my search.js file.
define([
'angular',
//backend services
'scripts/services/students/studentQueries',
'scripts/services/graph',
],function(angular){
console.log('app.js');
return angular.module('app',[
'studentQueries',
'graphQl'
]);
}
)
and finally I needed to add app.js into search.jsto:
'use strict'
define([
'angular',
'scripts/app'
],function (angular) {
angular.module('app')
.controller('schoolCtrl',['$scope','studentQueries'
//and the rest of logics

Lazy load AngularJS controllers with Webpack

I am using AngularJS 1.5.6 with Webpack.
My files architecture is:
app
|- scripts
| |- modules
| |- homepage
| |- homepage.js
| |- controller
| |- HomepageController.js
|- app.js
|- index.html
The webpack.config (please tell me if you need to see more of this file):
module.exports = {
context : __dirname + "/app",
entry : {
main: "./app.js",
vendor: [
'angular',
'angular-route'
]
},
[...]
};
index.html contains the import of the the js files:
<script type="text/javascript" src="vendor.js"></script>
<script type="text/javascript" src="main.js"></script>
In apps.js:
(function () {
'use strict';
require('./scripts/modules/homepage/homepage');
angular.module("NgAppProject", [
'ngRoute',
'ng',
'Homepage'
]);
})();
In homepage.js:
(function () {
'use strict';
angular.module('Homepage', [
'ngRoute',
'ng'
])
.config(['$routeProvider', '$controllerProvider', function($routeProvider, $controllerProvider) {
$routeProvider
.when('/home', {
templateUrl: 'scripts/modules/homepage/views/homepage.html',
controller: 'HomepageController',
controllerAs: 'homepageController',
resolve: {
deps: function() {
return require.ensure([], function (require) {
$controllerProvider.register("HomepageController", require('./controllers/HomepageController'));
}, '_homepage');
}
}
});
}]);
})();
The Problem:
When I start my app and go to localhost:8080/#/home, I get the error:
Error: [ng:areq] Argument 'HomepageController' is not a function, got undefined
I think it can't find my controller, but I can't figure out why.
However this works fine:
angular.module('Homepage', [
'ngRoute',
'ng'
])
.config(//config here)
.controller("HomepageController", require('./controllers/HomepageController'));
I would really welcome any help, thank you!
I think that require.ensure not return promise. So, you can try this approach:
resolve: {
deps: ['$q', function($q) {
var deferred = $q.defer();
require.ensure([], function(require) {
$controllerProvider.register("HomepageController", require('./controllers/HomepageController'));
deferred.resolve();
}, '_homepage');
return deferred.promise;
}]
}

Dealing With Dependencies in angularjs Testing

This is for work (I have permission) so I can't post exact code.
So I have to test controllers of a large module. The module has a large config function with a bunch of controllers for the logic of the different pages.
For the actual application it's loaded with bower, which is irritating since I'm testing with Karma-Browserify and npm. So the the dependencies are a mess. I basically have to import everything that was loaded from bower.json to package.json.
This is my karma.conf.js:
module.exports = function(config) {
config.set({
basePath: 'resources',
browserify: {
debug: true,
transform: [ 'browserify-shim' ]
},
browsers: [ 'PhantomJS' ],
captureTimeout: 60000,
client: {
mocha: {}
},
files: [
'tests/assist/test.js',
'assets/scripts/libs/logger.min.js'
],
frameworks: [ 'browserify', 'phantomjs-shim', 'mocha', 'chai' ],
port: 8080,
preprocessors: {
'tests/assist/controller.js': [ 'browserify' ]
},
reporters: [ 'mocha', 'coverage' ],
singleRun: true
});
};
So the code below this is my test.js (removing some company-specific names). Also I need to put angular.mock. or it won't work
require('angular');
require('angular-mocks');
//Main module needs these
jQuery = require('jquery');
require('angular-bootstrap');
require('angular-recaptcha');
require('angular-ui-router');
require('ngstorage');
require(**The path to the main module**);
require(**The path to a service it uses**);
require(**The path to a service it uses**);
require(**The path to a service it uses**);
describe('Blah', function () {
beforeEach(angular.mock.module('myApp'));
var $controller;
beforeEach(angular.mock.inject(function(_$controller_) {
$controller = _$controller_;
}));
describe('blahblah', function () {
it('sets loading to true', function () {
var $scope = {};
var controller = $controller('controller', {$scope: $scope});
assert($scope.showLoading === true);
});
});
});
The main module:
(function() {
'use strict';
})();
// Jquery noconflict
jQuery.noConflict();
var myApp = angular.module('myApp', ['ui.router', 'ngStorage', 'vcRecaptcha', 'ui.bootstrap']);
myApp.config(['$stateProvider', '$urlRouterProvider', '$locationProvider', function($stateProvider, $urlRouterProvider, $locationProvider) {
...
}])
.run([blah bunch of dependencies]) {
...
}]);
The controller (separate fie):
'use strict';
myApp.controller('controller', ['$scope', '$http', '$localStorage', 'service1', 'service2', 'service3',
function ($scope, $http, $localStorage, service1, service2, service3) {
..
}
...
As you can see I'm in dependency hell. I got the example test on the angular site to work, the main problem is with the dependencies and myApp not being visible to the controller. "ReferenceError: Can't find variable: myApp" in controllers/services
If anyone has a better way of going about testing I'm all ears.
This is not about dependency hell, not about testing also.
The code seems to rely on myApp global variable, this is strictly opposite to what Angular modules are for.
myApp should be a local variable that is defined dynamically in each function scope
(function () {
var myApp = angular.module('myApp', [...]);
...
})();
(function () {
var myApp = angular.module('myApp');
myApp.controller('controller', ...)
...
})();

AngularJS controller does not run on RequireJS config

I am trying to configure an web app with requireJS and angularJS. I come from marionette configuration and I am trying to have a similar one in angular (in concepts like views and controllers) first so I want to be able to map #/test to my controller and log in the console one message.
I've seen Does AngularJS support AMD like RequireJS? and RequireJS and AngularJS and I kind of got the differences and from my point of view my config should work... but it does not...
Here is my code:
File: app.config.js
require.config({
shim: {
angular: {
exports: 'angular'
},
angularRoute: ['angular']
},
paths: {
angular: '../lib/angular',
angularRoute: '../lib/angular-route'
}
});
require(['angular', 'app', 'routes/index'], function (angular) {
angular.bootstrap(document, ['app']);
});
File: app.js
define(['angular', 'angularRoute'], function (angular) {
//angular.module('app.controllers', []);
var app = angular.module('app', ['ngRoute']);
return app;
});
File: routes/index.js
define(['angular', 'app', 'controllers/index'], function (angular, app) {
app.config(['$routeProvider', function ($routeProvider) {
$routeProvider.when('/', { templateUrl: require.toUrl('/resources/js/app/templates/test.html'), controller: 'indexController'});
}]);
});
File: controllers/index.js
define(['angular', 'app'], function (angular, app) {
//var appControllers = angular.module('app.controllers');
app.controller('indexController', ['$scope', function ($scope) {
console.log('cascade...');
}]);
});
What am i missing? When i access #/test, i don't see "cascade" in the console, should I?.. Right?
Thanks in advance.

Angular + Requirejs - how to return multiple modules?

How can I return multiple angular modules in requirejs environment?
this is my app.js,
define([
'angular',
'angular-route',
'jquery'
], function (ng,ngRoute,$) {
'use strict';
console.log($('h1').length);
return ng.module('myApp', ['ngRoute']);
});
And I need a few more modules to return,
ng.module('myAppModule1', ['ngRoute']);
ng.module('myAppModule2', ['ngRoute']);
ng.module('myAppModule3', ['ngRoute']);
a controller example, for instance I want to get 'myAppModule3' in app.js,
define(['app'], function (app) {
var myAppModule = angular.module('myAppModule3');
myAppModule.controller('welcomeController', ['$scope', function($scope) {
//your minsafe controller
$scope.message = "Message from WelcomeController";
}]);
});
You could change app.js to return an object whose fields are the modules:
define([
'angular',
'angular-route',
'jquery'
], function (ng,ngRoute,$) {
'use strict';
console.log($('h1').length);
return {
myApp: ng.module('myApp', ['ngRoute']),
myAppModule1: ng.module('myAppModule1', ['ngRoute']),
myAppModule2: ng.module('myAppModule2', ['ngRoute']),
myAppModule3: ng.module('myAppModule3', ['ngRoute'])
};
});
And change your controller like this:
define(['app'], function (app) {
app.myAppModule3.controller('welcomeController', ['$scope', function($scope) {
//your minsafe controller
$scope.message = "Message from WelcomeController";
}]);
});
The generic (non-Angular specific) way is to use an object:
return {module1: /*..*/, module2: /*...*/ };
Then you just access to the values:
define(['app'], function (app) {
var module1 = app.module1;
});
However in Angular you just registered 'myAppModule1' in the Angular global. There is no need to do the object return, you can retrieve the registered module using the angular object:
define(['angular'], function (angular) {
var module1 = angular.module('myAppModule1');
// without extra parameter it tells angular to retrive an existing
// module
});
Update: I just realize that you did it in your code. It didn't worked? Maybe be you have a dependency issue, make sure that app.js is loaded first.

Categories