I had created multiple modules in a page and make it nested such as
module A{
module B{
.....
}
}
izit possible passing value from module A to module B? I refer to this website http://www.simplygoodcode.com/ to create multiple modules in one page.
You can specify module A as a dependency of module b.
e.g.
var moduleA = angular.module('module-a', []);
// ...
var moduleB = angular.module('module-b', ['module-a']);
Related
My use case is: we have several helper classes, A and B, that are services, A depends on B, and I wanted to make them providers so that they can be used in .config phase.
I followed this SO answer to load a provider inside a provider.
As you can see here, it works:
http://plnkr.co/edit/SIvujHt7bprFumhxwJqD?p=preview
var coreModule = angular.module('CoreModule', []);
coreModule.provider('Car', function() {
//CarProvider.engine
this.engine = 'big engine';
//Car
this.$get = function() {
return {
color: 'red'
};
};
});
coreModule.provider('ParameterService', ['$injector', function($injector) {
try {
var CarProvider = $injector.get('CarProvider');
this.deepEngine = CarProvider.engine;
console.log('deepEngine = ' + this.deepEngine);
} catch (e) {
console.log("nope!")
}
// ParameterService
this.$get = function() {
return {};
};
}]);
coreModule.config(function(CarProvider) {
console.log('configEngine = ' + CarProvider.engine); // big engine
});
This works if I have Car and ParameterService in one file in this order.
However when I split Car and ParameterService into multiple files on disk, or I define ParameterService before Car in the same file, $injector.get('CarProvider') inside ParameterService fails.
How do I fix the issue?
I want to have one provider/service per file and I don't understand what is missing.
The order in which the services are defined doesn't matter during run phase, where service instances are injected. But it does matter during configuration phase, where service providers are injected, i.e. in provider constructors and config blocks.
Providers and config blocks are executed in the order in which they are defined. If Car provider is defined after ParameterService provider or config block, CarProvider doesn't exist at the moment when those two are executed.
To avoid potential race conditions, one module per file pattern should be followed. This allows to keep the app highly modular (also beneficial for testing) and never care about the order in which the files are loaded. E.g.:
angular.module('app', ['app.carService', 'app.parameterService']).config(...);
angular.module('app.carService', []).provider('Car', ...);
angular.module('app.parameterService', []).provider('ParameterService', ...);
Module parts are executed in the order in which the modules are defined in angular.module array hierarchy, from children to parents.
The decision if config block needs its own module depends on what it does (mostly for testing reasons).
It is possible to have providers in different files. You just need to attach them to the first module that you created.
If your markup looks like this:
<script src="coreModule.js"></script>
<script src="parameterService.js"></script>
Then, in coreModule.js, define your module:
angular.module('CoreModule', [])
.provider('Car', function() {
...
}
Remember, the second parameter ([]) tells angular to create a new module.
Then, declare your other provider in a different file, and attach it to your existing 'CoreModule' module:
angular.module('CoreModule')
.provider('ParameterService', ['$injector', function($injector) {
...
}
Notice that we are only passing one parameter to .module(). This tells angular to add your provider to an existing module.
Plunkr Demo
I'm doing some studies using the Pixijs library, which I find amazing. I'll also have a look into Fabricjs, that seems to have a smaller footprint.
I've been working with Angularjs for some time now and I like conventions, instead of taking time in each project doing configuration and organizing code differently every time.
I would like to hear from some body who experienced Pixijs (or similar) with a framework to organise the code.
I understand that Angularjs is MVVM, but let me know about any tips or suggestion that you may think of?
I did some research this far and a few things came to my mind, such as Browserify (I do believe in convention instead of configuration like I've mentioned though and maybe this wouldn't be the best tool for me).
Kinda old question, but this is something I was looking for myself when starting out with PIXI, so I hope it could be of help to someone to get started.
I use the Revealing module pattern and separate the application into separate files/modules, and then use Browserify to create the application bundle. The HTML loads the app.js bundle which stems from the app.js source below.
index.html: Load your libs (PIXI et al) in <head> and then your app.js in the <body>.
app.js source example:
(function() {
// App.js is the "bootstrap" that loads dependencies, takes care of pre-loading etc.
// I have a template of this which I copy into any new project and use as a checklist.
var core = require("./core.js"); // Use a dummy module as application-wide namespace for easy access
// Any external modules (f eg node modules) could go here
core.utilityLib = require("node-lib");
// Application modules here
core.myModule = require("./myModule.js");
// core.myModule2 = require("./myModule2.js"); // .. you get the idea
// Our main application module
core.main = require("./main.js");
// Init function to run when DOM/scripts have loaded
var init = function() {
// I have a generic screen module which sets up PIXI renderer depending on device compatibility using Modernizr (or weapon of choice). To keep it simple for the sake of the example, lets just create our renderer directly:
core.renderer = PIXI.autoDetectRenderer(screen.innerWidth,screen.innerHeight,{resolution:window.devicePixelRatio});
// I also use a generic loader module that wraps PIXI.loader, taking a list of assets from a config file. Let's just call PIXI.loader directly for now:
PIXI.loader
.add({name:"myasset",url:"/myasset.png"})
.on('progress', loadProgressFunction)
.once('complete',loadCompleteFunction)
})
.load();
}
window.onload = init; // Tell browser to call init function when loaded
// Optional loading progress bar
var function = loadProgressCallback(e) {
}
// Call when mandatory assets has been loaded
var loadCompleteFunction = function() {
myModule.init(); // Init any mandatory modules, f eg to instantiate a player
main.init(); // Tell our main application/game module that we're ready to do fancy stuff
}
// Method to make things move
var animate = function() {
// Send juice to modules that needs to be juiced (or use a ticker module on per-module basis).
// core.main.animate();
requestAnimationFrame(animate);
}
requestAnimationFrame(animate); // See comment below
}());
Comment: PIXI has an built-in requestAnimationFrame alias that takes care of fallback. If not using PIXI, you could use Paul Irish' gist.
core.js:
module.exports = {}; // Just a dummy object to create a module scope that all the modules
// can use to communicate with each other, without running into circular reference problems
main.js:
// Main application/game module
module.exports = (function() {
// Dependencies
var core = require("./core.js"); // This way we can easily access all the necessary modules
// Exports
var exports = {}; // Everything put into this object will be "public"
// Vars
var stuff = 1; // Module vars
exports.init = function() {
// Application magic starts here :)
}
// Some other public method ...
exports.publicMethod = function() {
}
// Some private method
var privateMethod = function() {
}
return exports; // Expose public functions to other modules
}());
Any additional modules can be organized in pretty much the same way as main.js.
Run browserify dev/app.js > html_root/app.js each time you want to "compile" your bundle (or create a Makefile, gulp-, node-, or webpack-script - whichever you prefer).
As I understood there is no classical registration of module, where we can inject our dependencies like:
var myModule = angular.module('myModule', [otherModule]);
However there are file module-name.client.module.js at the root of the directorys
'use strict';
// Use Applicaion configuration module to register a new module
ApplicationConfiguration.registerModule('module-name');
Can I inject here my module like *.registerModule('module-name', [someModule]); or I should do it in angular.module('articles').config(...)?
But in config I can inject only providers, without factories
As far as i used angular, the best practice to load custom module and external library
is to combine angularjs with requirejs.
It's done in 3 step.
First, load in your main html file a js file which builds the foundation of your system (require.config):
Here you have all the parameters allowed : https://github.com/jrburke/r.js/blob/master/build/example.build.js
Example :
In your html file :
<script src="path/to/require.js" data-main="path/to/yourBuildFile"></script>
In yourBuildFile file :
require.config({
// you can define suitable names for all your external js library
paths:{
// don't put a ".js" at the end of the file
'angular' : 'path/angular',
'underscore' : 'path/underscore-min',
'...': '...',
},
// and other useful things
});
Second, in the same file or in another (see the parameter deps in link above), bootstrap your app:
Like explained here : https://docs.angularjs.org/guide/bootstrap
Example:
// AMD module injection, more info here : http://requirejs.org/docs/whyamd.html
define([ // inject all the external library you need + the definition of your app
'angular',
'require',
'yourpath/yourApp' // don't bother here, it's explained in third point
], function(angular){
// link your app to the document file programatically
angular.bootstrap(document, ['nameOfYourApp']);
});
Third, you define your app (in the "yourpath/yourApp")
Example:
define([
'angular',
// put all path to your directives + controllers + services
], function(angular){ // only specify parameters the library you use in the function
// you create your sublime app :)
angular.module('nameOfYourApp', [
// put all the name of your modules injected above
]);
});
The example above is made for single page application.
You can find other examples for multipage application here
http://requirejs.org/docs/start.html
In Angular 1.2, ngRoute is a separate module so you can use other community routers like ui.router instead.
I'm writing an open-source module that aims to work for multiple different router implementations. So how can I check which router is loaded or exists?
I'm doing the following inside a factory in my module, but it does not work the way I expect it to:
if (angular.module("ngRoute"))
// Do ngRoute-specific stuff.
else if (angular.module("ui.router"))
// Do ui.router-specific stuff.
It raises an error for whichever module is not loaded. For example, if the app is using ui.router, then the following error is raised for the ngRoute check:
Uncaught Error: [$injector:nomod] Module 'ngRoute' 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.
I am not aware of a way of checking without an error being raised; however, notice that the issue is that it was an Uncaught Error, not that an error was thrown. The pattern for catching such an error is the following.
try { angular.module("ngRoute") } catch(err) { /* failed to require */ }
If an error is caught, you can try the other module, and if not, you can use the first.
If your behavior will be the same for each module, you could do something like the following, in which we define a function which will attempt the first of the listed module names, and if an error is thrown, try the next option.
var tryModules = function(names) {
// accepts a list of module names and
// attempts to load them, in order.
// if no options remain, throw an error.
if( names.length == 0 ) {
throw new Error("None of the modules could be loaded.");
}
// attempt to load the module into m
var m;
try {
m = angular.module(names[0])
} catch(err) {
m = null;
}
// if it could not be loaded, try the rest of
// the options. if it was, return it.
if( m == null ) return tryModules(names.slice(1));
else return m;
};
tryModules(["ngRoute", "ui.router"]);
I would test for the service instead of the module itself.
// In controller
if($injector.has('$route')){
}
if($injector.has('$state')){
}
// In angular config
if($injector.has('$routeProvider')){
}
if($injector.has('$stateProvider')){
}
The original answer is legit. However, as an alternative, I wrote this when I needed to "find or create" the modules. There's a number of use cases, but generally, it lets you not have to worry about file load order. You could either put this in a initialModules.js... or the top of all your individual service/directive files start with something like this. This little function works like a charm for me:
var initialModules = [
{name: 'app.directives', deps: ['ui.mask']},
{name: 'app.services'},
{name: 'app.templates'},
{name: 'app.controllers'}
];
initialModules.forEach(function(moduleDefinition) {
findOrCreateModule(moduleDefinition.name, moduleDefinition.deps);
});
function findOrCreateModule(moduleName, deps) {
deps = deps || [];
try {
angular.module(moduleName);
} catch (error) {
angular.module(moduleName, deps);
}
}
///// OR... in like "myDirective.js"
findOrCreateModule('app.directives').directive('myDirective', myDirectiveFunction);
If you decorate angular.module to store the names in an array then you could just check if the array contains your module name.
Decorate angular.module
See #dsfq's answer on SO.
This needs to happen after angular is loaded but before you start loading any angular modules.
Check for your module
if(angular.modules.indexOf("ngRoute") > -1) ...
The problem of automatically load or create a module could be better solved by something like gulp-angular-filesort, though.
It works really flawlessly.
From gulp-angular-filesort github page:
Automatically sort AngularJS app files depending on module definitions and usage
Used in conjunction with gulp-inject to inject your AngularJS application files (scripts) in a correct order, to get rid of all Uncaught Error: [$injector:modulerr].
Disclaimer: I'm not affiliated with gulp-angular-filesort, I only use it with a lot of profit.
A much better solution is to simply do your check when the module is created. You just need a utility function to add a callback.
//create a utility function to add a callback to object methods
//here we are making it a method of the underscore or lowdash object
//but it could be added to the angular global object or anything else
_.addCallBack = function (obj, originalMethodName, callBackMethod, context){
var fnOriginal = obj[originalMethodName],
outcome;
context = context || obj;
obj[originalMethodName] = function () {
var outcome = fnOriginal.apply(this, arguments);
callBackMethod.apply(this, arguments);
return outcome;
};
};
_.addCallBack(angular, "module", function(sModuleName, asDependencies){
if(_.contains(asDependencies, "ngRoute")){
//your logic here
//just loop through if you don't use underscore or lowdash
}
});
AngularJS 1.6.3 and up has a way to check if a module is loaded via the $injector service.
Also added in 1.6.7 was the ability to load new modules which may be of interest to some.
Consider the following jfiddle http://jsfiddle.net/bchapman26/9uUBU/29/
//angular.js example for factory vs service
var app = angular.module('myApp', ['module1', 'module2']);
var service1module = angular.module('module1', []);
service1module.factory('myService', function() {
return {
sayHello: function(text) {
return "Service1 says \"Hello " + text + "\"";
},
sayGoodbye: function(text) {
return "Service1 says \"Goodbye " + text + "\"";
}
};
});
var service2module = angular.module('module2', []);
service2module.factory('myService', function() {
return {
sayHello: function(text) {
return "Service2 says \"Hello " + text + "\"";
},
sayGoodbye: function(text) {
return "Service2 says \"Goodbye " + text + "\"";
}
};
});
function HelloCtrl($scope, myService) {
$scope.fromService1 = myService.sayHello("World");
}
function GoodbyeCtrl($scope, myService) {
$scope.fromService2 = myService.sayGoodbye("World");
}
I have 2 modules (module1 and module2). Both module1 and module2 define a service called myService. This appears to create a name clash on myService within Angular when both modules are imported into myApp. It appears AngularJs just uses the second service definition without warning you of the possible issue.
Very large projects (or just reusing modules in general) would have a risk of names clashing, which could be difficult to debug.
Is there a way to prefix names with the module name so that name clashes don't happen?
As of today, AngularJS modules do not provide any sort of namespacing that would prevent collisions between objects in different modules. The reason is that an AngularJS app has a single injector that holds names for all objects without respect to module names.
The AngularJS Developer Guide says:
To manage the responsibility of dependency creation, each Angular
application has an injector. The injector is a service locator that is
responsible for construction and lookup of dependencies.
As you've mentioned, nasty bugs can result when injecting modules into your main/app module. When collisions happen they are silent and the winner is determined by whichever was the last module injected.
So no, there's not a built in way of avoiding these collisions. Maybe this will happen in the future. For large apps where this problem becomes more likely, you're right that naming conventions are your best tool. Consider whether objects belonging to a module or function area might use a short prefix.
You can avoid this situation by using a convention to name your modules so that they always unique.
One approach is to look at how other languages do it. For example in Java the “full name” of the class is based on the name of the file and the folder it’s in. For example if you had a Java file called Bitmap.java in the folder MyArtStuff, the full name of the class would be MyArtStuff.Bitmap
Turns out AngularJS allows you to have dots (.) as part of your module name so you could essentially use the name convention.
For example if a developer create a module called “ModuleA” in the script “MainPage\Module1.js” they should name their module “MainPage.Module1.ModuleA”. Because each path and filename is unique in your app then your module name will be unique.
You would just have to get your developers to follow this convention.
Note as Rockallite points out this will not help with services, controllers, etc having the same name in multiple modules. But you can use a similiar approach to result that and prefix the names of those elements as well.
Ideally AngularJS would have namespaces and in the future it might. Until then the best we can do is do what developers have been doings for over 40 years before namespaces were invented and prefix our elements best we can.
Unfortunately, there is no namespacing in AngularJS. One solution is to use a prefix (another solution may be this!). See the following example:
// root app
const rootApp = angular.module('root-app', ['app1', 'app2']);
// app 1
const app1 = angular.module('app1', []);
app1.controller('app1.main', function($scope) {
$scope.msg = 'App1';
});
// app2
const app2 = angular.module('app2', []);
app1.controller('app2.main', function($scope) {
$scope.msg = 'App2';
})
<!-- angularjs#1.7.0 -->
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.7.0/angular.min.js"></script>
<!-- root app -->
<div ng-app="root-app">
<!-- app 1 -->
<div ng-controller="app1.main">
{{msg}}
</div>
<!-- app 2 -->
<div ng-controller="app2.main">
{{msg}}
</div>
</div>
Define your controllers on the module you want the service to be from.
service2Module.controller("ServiceTwoCtrl", function(myService, $scope) {});