My Angular app structure is like this:
App.js
angular.module('RateRequestApp', [
'RateRequestApp.services',
'RateRequestApp.controllers',
'ui.bootstrap',
'angular-loading-bar',
'textAngular',
'angularFileUpload'
]);
I am using different HTML files for different pages and I am not using
Angular's $route, but still I want to use same app in all pages with different controllers.
As you can see I am injecting third party modules to my app. The problem is that in some pages I don't want some of this modules, How can I avoid them where I don't need them?
Yes you can do it something like this:
var myApp = angular.module('RateRequestApp', [
'RateRequestApp.services',
'RateRequestApp.controllers',
'ui.bootstrap',
'angular-loading-bar'
]);
var lazyModules = ['textAngular', 'angularFileUpload'];
angular.forEach(lazyModules, function(dependency) {
myApp.requires.push(dependency);
});
In this case you may inject the modules conditionally. (But please note, lazy loading of some module may not work where there is some configuration needed.)
Related
I have an app module defining dependencies (taken from phonecat-Tutorial) in the directory app:
app.module.js:
angular.module('phonecatApp', [
'phoneList' // (*)
]);
Then, in a directory app/phone-list are the module:
phone-list/phone-list.module.js:
angular.module('phoneList', []);
and component:
phone-list/phone-list.component.js:
angular.module('phoneList').component('phoneList', {...});
From registering the module phoneList in the first snippet at (*), how does AngularJS knows where to fetch the module from? Is the mapping from phoneList to phone-list-directory naming a builtin feature?
AngularJS has a dictionnary of modules. The key in this dictionnary is the string that you give your module when you register it with the following line :
angular.module('phoneList', []);
That's why the previous line must always have been executed in javascript before you can use your module by adding a component (like following line) :
angular.module('phoneList').component('phoneList', {...});
or by injecting your module into another module
angular.module('phonecatApp', [
'phoneList'
]);
The code for relevant modules has to be included in scripts included in the page. This is either done by manually adding various script tags or by task runners like grunt, gulp, webpack etc adding them for you.
If dependency module code doesn't exist angular will throw error
In simpler terms - where the code comes from is not angular's responsibility. It has to already exist for angular to work
I'm working on an Angular project that will require modularisation and I will be creating many small modules for it. Example: myApp.core, myApp.components.component1 myApp.components.component2 etc. each being its own file compiled into one with gulp. When I inject myApp into another project, I want to be able to consume all modules in one myApp injection, like so:
angular.module('project2', ['myApp'])
instead of:
angular.module('project2', [
'myApp.core',
'myApp.components.component1',
'myApp.components.component2'
])
Much like the Angular team have done with Material. Everything is its own module, but in order to use Angular Material, you only have to add ngMaterial as a dependency.
angular.module('project2', ['myApp']);
angular.module('myApp', ['components']);
angular.module('components', ['component1','component2','component3']);
angular.module('component1', []);
angular.module('component2', []);
angular.module('component3', []);
I have a bit of an unorthodox angular set up, and I'm starting to doubt if it's going to work.
I've split the web-app that I'm building into 5 apps. Each will have it's own module, and it's own gulp build that will compile that module into a minified js file that will be included in the html. There will be 1 js build per page.
Then, I have a sharedService module. Each of the other modules will have this sharedService module injected into it. The shared service has it's own gulp build, and is being included in the master template that every other page is inheriting from, so every page will have this shared template js build on it.
I'm finding now though that changes to the sharedService in one app, isn't reflected when I navigate to the other. I'm thinking each app is using it's own version of the sharedService and it's not actually being shared at all, or to be more specific, when navigating to the next app the page refreshes and reloads the services losing the data that existed within them.
I'm using typescript and browserify.
An example of one of my apps:
'use strict';
declare var angular, require:any;
angular.module('compareApp', ['ui.router', 'sharedServices']);
// require module dependencies
require('./compareAppConfig.ts');
require('./compareController.ts');
The shared Service app:
'use strict';
declare var angular, require:any;
angular.module('sharedServices', []);
require('./services/dogService.ts');
require('./services/userService.ts');
And an example of one of the services:
declare var require, angular:any;
angular.module('sharedServices').service('userService', userService);
const API_PATH = "/api/";
function userService($http, $q) {
var userData = {
favorites: []
};
function getUser(){
return userData;
}
function saveFavorite(dogId){
userData.favorites.push(dogId);
}
return {
saveFavorite: saveFavorite,
getFavorite: getFavorite
};
}
export = userService;
So basically when I save a dog to the favorites array in one app, and then navigate to another app, that array is empty.
When you say split the webapp do you mean separate html files?
If you are navigating to an entirely separate page/html file then your code is going to be re-run when the page loads. Therefore any data in your shared service gets wiped.
If you would like to route to different pages of your application while maintaining state I suggest looking into client-side routing using ui-router.
I have a main module, that loads ngRoute service.
angular.module("app", ["ngRoute", "app.settings"]
and my app.settings module is not loading ngRoute service,
angular.module("app.settings", [])
.config(["$routeProvider", function($routeProvider){
$routeProvider.when("/settings", {
template: "{{message}}",
controller:"SettingsController"
});
}])
But I can use $routeProvider in this module.
Does angular module loading order not care? Can I load any dependency any module?
The thing is that your app module is loading the ngRoute, and also is loading your app.settings modules, so the dependency is already injected into your Angular application, so there is no need to injected again.
Does angular module loading order not care?
The order doesn't matter Angular first resolve the dependencies and then compiles the modules, controllers etc.
angular.module("app", ["ngRoute", "app.settings"]
Is the same as
angular.module("app", ["app.settings", "ngRoute"]
However you can run into troubles in some Unit Test scenarios if you only load the module app.settings your test will fail. But in the most cases you are going to load the app module and all the main modules of your Angular application.
Can I load any dependency any module? Short answer, yes.
Long answer: Your ngRoute dependency should be loaded in the main module because is something which your app module is going to need it to define the basic routing, if the dependency is loaded in several modules is not going to throw any error, in fact you should add all the dependencies needed by each module because in large applications there is no guarantee that the ngRoute/myFactory/etc is already loaded.
Update to improve readability
I am trying to get started with Karma tests, adding them to an existing Angular app.
This is my main app definition file:
angular
.module('myApp', [
'ngRoute',
'moduleAdherence'
]);
This is my controller file:
angular
.module('moduleAdherence', [])
.controller('AdherenceCtrl', ['$scope', function ($scope) {
$scope.awesomeThings = [1,2,3,4];
}]);
This is my first stab at a file:
describe('Controller: AdherenceCtrl', function () {
beforeEach(module('myApp'));
var MainCtrl,
scope;
beforeEach(inject(function ($controller, $rootScope) {
scope = $rootScope.$new();
MainCtrl = $controller('AdherenceCtrl', {
$scope: scope
});
}));
it('should attach a list of awesomeThings to the scope', function () {
expect(scope.awesomeThings.length).toBe(4);
});
});
When I try to run this with grunt test, it fails with the following error:
Uncaught Error: [$injector:nomod] Module 'd3' 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.
http://errors.angularjs.org/1.2.0/$injector/nomod?p0=d3
at /Users/me/Dropbox/projects/myapp/app/bower_components/angular/angular.js:1498
I don't understand this, because this controller does not Use D3. I do use D3 elsewhere in the app, in a directive, but I'm not registering it with the module (I use the external D3 file).
Why is Karma noticing D3? Shouldn't it be able to test this controller without D3?
In karma configuration file (karma.conf.js) you need to define all libraries.
etc.
// list of files / patterns to load in the browser
files: [
'app/lib/angular/angular.js',
'app/lib/angular-route/angular-route.js',
'test/lib/angular-mocks.js',
'app/app.js',
'app/controllers/*.js',
'app/services/*.js',
'app/*',
'test/spec/**/*.js'
],
Had a similar problem and my solution (inspired someway by #danba comment) was to load the scripts in the files in the exact same order as they were loaded in the index.html.
In my case globbing patterns like app/scripts/**/*.js were causing trouble to karma which constantly threw errors.
Maybe not the most elegant solution to copy all the scripts definitions but worked in the end so my test could finally get back running. Hope it helps.
EDIT: Editing this because today I probably (and hopefully) got the hang of what was going wrong here. So it seems that Karma does not like globbing patterns when the same module is defined in one file and used in many different files. Let's say that your folder structure is like this:
Suppose that in AuthService.js you have your module definition for all your services in that folder, so that that file starts with:
angular.module('myApp.services', [
'myApp.urls',
'ngCookies'
])
Then in all the other files you're just attaching other services to that same module. In the picture tokenService.js would start with:
angular.module('myApp.services');
If everything stays this way everything will probably work. But if by any chance I define the module the other way round, so the module definition is not anymore in the first file of that folder but on another that Karma reads after AuthService then it will throw an error and refuse to complete the tests.
Solution 1
A solution might be to put the module definition in its own file, starting with an underscore. Finally, let all the siblings files depend on that one(s). So the above folder structure should be:
Solution 2
Another - probably better - solution is to mark the files where the modules are defined with a common suffix/extension for example service.module.js while the files that depend on it could be named normally like authService.js, tokenService.js.
The Karma configuration at that point will become something like:
// list of files / patterns to load in the browser
files: [
'app/lib/angular/angular.js',
'test/lib/angular-mocks.js',
'app/app.js',
'app/**/*.module.js', // <-- first the module definitions...
'app/**/*.js', // <-- ..then all the other files
'test/spec/**/*.js'
],
This way karma will load the modules definitions first and then those file that depend on them.
I also had the same issue, in my case problem occured because there were several files per module which is a bad practice since we can point to not initialized module. Solved this by including just one file per module.
Always load the non minified version of angular into karma.
it will display errors and help you better to find out what to change.
In your case, it's the order of the files being loaded by karma.
Check your index.html that you don't include something by a script tag like.
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5angular-resource.js"
All your Javascript file need to be in your karma.config.js
Here's a simple solution that worked for me, based on above Solution #1 by #Nobita, AND #danba's comment.
In karma.conf.js, explicitly load the prerequisite file above the pattern that pulls in the rest:
files: [
...
'app/module.js',
'app/*.js'
]
Karma does not seem to mind that the pattern also matches 'module.js'.