I'm pretty new to Angular. Sorry for the basic question but I couldn't find info about how to write a library in Angular the proper way.
The reason is to benefit from Angular modules as well as testability/mocks.
The library
I am transferring my code to Angular but a big part of my code is written based on a library that I've developed over a couple of months. Imagine something like underscore.
So my initial thought was to wrap it in an angular module like:
angular.module('mylib', [])
.factory('map', map)
.factory('reduce', reduce)
.factory('find', find);
function map (...
Each factory function is testable and stuff. And then use it like:
angular.module('app', ['mylib'])
.controller('MainCtrl', ['$scope', 'map', MainCtrl]);
function MainCtrl($scope, map) {
... do stuff with map ...
This is fine for a couple of functions I guess but my library has over 20 functions and it's just too much boilerplate to define every one of them as a factory and then inject them into my controller.
Second solution
Another way is to have just one factory that returns an object. Kinda like this:
angular.module('mylib', [])
.factory('mylib', mylib)
function mylib () {
var exports = {};
exports.map = function (...
exports.reduce = function (...
exports.find = function (...
return exports;
}
And then use it like this:
angular.module('app', ['mylib'])
.controller('MainCtrl', ['$scope', 'mylib', MainCtrl]);
function MainCtrl($scope, mylib) {
... do stuff with mylib.map ...
But...
Is this a good approach? What would a good way to write a utility library in Angular and use it? Readability and maintainability are important but I prefer not to have so much boilerplate code.
If your library functions are as generic as your example, I wouldn't try to define them as Angular modules in the first place. Write a plain JS library. That will result in less useless boilerplate, and as a bonus your library will also be usable in non-Angular apps. Angular plays along nicely with libraries that are unaware of it, for example by not requiring data objects to implement anything extra (think ko.observable, Backbone Model, etc) to be usable with data binding.
Being an Angular module is not a requirement for testability. The only thing I can think of that Angular could do to improve testability in a generic utility library is the dependency injection (e.g. injecting a mock service for "current time" into a time library for deterministic results). If you don't need any of that, take the past of least complication and just write a plain JS library.
If wrapping the library in an Angular module is an absolute requirement (for instance to not pollute the global namespace with a short namespace for it), I would still write the library in plain JS, put it into a more complicated namespace, and then alias it with an Angular module.
lib/handy-utilities-1.0.js
// Or any other descriptive namespace
var handyUtilities = {
map: function() { ... }
};
app/util.js
angular.module("handyUtilities", []).constant("util", handyUtilities);
Related
I need to use this NoSleep.js inside my AngularJs controller. I installed NoSleep using bower but I need to inject it to my angularjs controller, but I have no idea on how do it, NoSleep isn't an angular service and I need to use this declaration var noSleep = new NoSleep(); in order to use it.
I'm using babel, webpack, gulp, karma
Any advise?
Typically when using non-angularjs libraries in an Angularjs application they are wrapped in a module. Then you inject the module into your app and then inject the service/factory you've wrapped with this into your controllers as needed, so you'd create a module that looks something like this:
(() => {
"use strict"
angular.module('nosleep.module', []).factory('nosleep', ['', function () {
return new NoSleep()
}])
})()
Then you'd inject the module into your main application module with something like:
angular.module('myApp', '[nosleep-module')
Then in your controllers that require access to nosleep you'd inject nosleep.
class myController {
constructor(nosleep) {
this.nosleep = nosleep
}
}
Then from within your control your can address it with this.nosleep.
It looks like NoSleep just declares a function in the global scope, so you could do something like this:
NoSleepService.js
(function () {
// place noSleep minified code here
angular.module('myApp').service('noSleep', NoSleep);
})()
That will create a NoSleep object within the enclosing function so that it doesn't pollute the global scope and register a new instance of it to your angular app.
If you have any JS lib and it's not an Angularjs lib, just add it using script tag in the html file and call it like you used to before they create AngularJs.
Have you tried it ?
Or go to the other way and wrap the lib with AgnularJs service (write an AngularJs service and call the lib method inside it), then inject this new service anywhere you want.
Update:
What #Mike Feltman and #richbai90 said in their answers was good and maybe enough for you, but I think it's better to build your service and call a methods you created, but inside these method use what ever library you want (it'll be like using interfaces in C#):
(function() {
'use strict';
angular.module('nosleep.module', []).service('nosleep', ['', nosleepFunc]);
function nosleepFunc() {
var nosleepObj = new NoSleep();
var service = {
method1: method1,
method2: method2
//... etc
};
function method1() {
return nosleepObj.method1();
}
// ..... other methods
return service;
}
})();
This way if you wanted to change nosleep lib to another one, and that new one lib has another method names, you need only to change in your service code.
What 's the difference with the modularisation of a javascript code (with browserify by example) and the dependency injection?
Are they synonymes? Are the two going together? Or Am I missing some point?
Modularisation refers to breaking code into individual, independent "packages".
Dependency injection refers to not hardcoding references to other modules.
As a practical example, you can write modules which are not using dependency injection:
import { Foo } from 'foo';
export function Bar() {
return Foo.baz();
}
Here you have two modules, but this module imports a specific other hardcoded module.
The same module written using dependency injection:
export function Bar(foo) {
return foo.baz();
}
Then somebody else can use this as:
import { Foo } from 'foo';
import { Bar } from 'bar';
Bar(Foo());
You inject the Foo dependency at call time, instead of hardcoding the dependency.
You can refer this article:
Modules are code fragments that implement certain functionality and
are written by using specific techniques. There is no out-of-the box
modularization scheme in the JavaScript language. The upcoming
ECMAScript 6 specification tends to resolve this by introducing the
module concept in the JavaScript language itself. This is the future.
and Dependency injection in JavaScript
The goal
Let's say that we have two modules. The first one is a service which
makes Ajax requests and the second one is a router.
var service = function() {
return { name: 'Service' };
}
var router = function() {
return { name: 'Router' };
}
We have another function which needs these modules.
var doSomething = function(other) {
var s = service();
var r = router();
};
And to make the things a little bit more interesting the function
needs to accept one more parameter. Sure, we could use the above code,
but that's not really flexible. What if we want to use ServiceXML or
ServiceJSON. Or what if we want to mockup some of the modules for
testing purposes. We can't just edit the body of the function. The
first thing which we all come up with is to pass the dependencies as
parameters to the function. I.e.:
var doSomething = function(service, router, other) {
var s = service();
var r = router();
};
By doing this we are passing the exact implementation of the module
which we want. However this brings a new problem. Imagine if we have
doSomething all over our code. What will happen if we need a third
dependency. We can't edit all the function's calls. So, we need an
instrument which will do that for us. That's what dependency injectors
are trying to solve. Let's write down few goals which we want to
achieve:
we should be able to register dependencies
the injector should accept a function and should return a function which somehow gets the needed resources
we should not write a lot, we need short and nice syntax
the injector should keep the scope of the passed function
the passed function should be able to accept custom arguments, not only the described dependencies
A nice list isn't it. Let's dive in.
I have two third-party modules, both defining a factory with the same name. Obviously, I don't have any control over the naming of those modules without resorting to a kludge.
Additionally, I have two further, internal modules, each using a different one of the two third-party modules as a dependency (as below). I was sure that I was unable to access components from a module not listed in current module's dependencies, but it turned out I was wrong.
Here even if own1 depends on thirdParty1 (which has hello defined as hello world) it's getting hi there (from thirdParty2) in controller. The same is for the other modules' pair.
Is there any way to "isolate" modules so I can only use stuff that I explicitly depend on? If not, what's the point of having modules if I can reach anything at any time (assuming main app module has it as its dependency)? Also if I have two modules with components named hello how can I tell which is gonna be used?
Here is jsbin for that http://jsbin.com/vapuye/3/edit?html,js,output
angular.module('app', ['own1', 'own2']);
//third-party modules
angular.module('thirdParty1', []).factory('hello', function () {
return 'hello world';
});
angular.module('thirdParty2', []).factory('hello', function () {
return 'hi there';
});
// "own" modules
angular.module('own1', ['thirdParty1']).controller('Own1Ctrl', function(hello) {
this.greet = hello;
});
angular.module('own2', ['thirdParty2']).controller('Own2Ctrl', function(hello) {
this.greet = hello;
});
And the result of:
<body ng-app="app">
<div ng-controller="Own1Ctrl as own1">
Own1: {{ own1.greet }}
</div>
<div ng-controller="Own2Ctrl as own2">
Own2: {{ own2.greet }}
</div>
</body>
Is :
Own1: hi there
Own2: hi there
You can request a factory of a certain module explicitly (without dependency injection):
var injector = angular.injector(['thirdParty1']);
var hello1 = injector.get('hello');
var injector = angular.injector(['thirdParty2']);
var hello2 = injector.get('hello');
You can also use this, to wrap the third party factories into own factories:
angular.module('own1', ['thirdParty1']).factory('hello1', function () {
var injector = angular.injector(['thirdParty1']);
var hello = injector.get('hello');
return hello;
});
angular.module('own2', ['thirdParty2']).factory('hello2', function () {
var injector = angular.injector(['thirdParty2']);
var hello = injector.get('hello');
return hello;
});
This allows you to use hello1 and hello2 in all other parts of your application.
Since there is no built-in name spacing of modules (or components of modules) the best way to achieve your goal is to use a unique naming convention for your modules. Most libraries for angular do this, and then you should be good to go.
Besides encapsulating your applications behavior, modules help testing and mocking your application.
I dont think it is possible for angular to differentiate between two components that are named the same (I think this changes with angular 2). And I might argue that two components that are named the same might do the same and you should look why you need both?
The following code is working fine before minifiaction. Essentially, editableOptions is an angular value provided by the angular-xeditable library.
angular.module('common').run(function(editableOptions) {
editableOptions.theme = 'bs3';
}
)
However, when I minify, I get an injection error. I believe this is because editableOptions is getting minfied. How can I reference it differently so that this does not occur? Is there some way of referencing it starting with angular.module('xeditable') ?
Use the minification-safe method of defining injected objects:
angular.module('common').run(
['editableOptions',
function(editableOptions) {
editableOptions.theme = 'bs3';
}
]);
Angular team have a solution to this problem mentioned in one of their tutorials:
https://docs.angularjs.org/tutorial/step_05
A Note on Minification
Since Angular infers the controller's dependencies from the names of arguments to the controller's constructor function, if you were to minify the JavaScript code for PhoneListCtrl controller, all of its function arguments would be minified as well, and the dependency injector would not be able to identify services correctly.
We can overcome this problem by annotating the function with the names of the dependencies, provided as strings, which will not get minified.
This is another style to avoid minification and uglification problems:
function runModule(editableOptions) {
editableOptions.theme = 'bs3';
}
runModule.$inject = ['editableOptions'];
angular.module('common').run(runModule);
Todd Motto Angular Style
Guide
This aids with readability and reduces the volume of code "wrapped"
inside the Angular framework
I'm building an app that has a single global namespace like so:
var myApp = {};
I then have a bunch of different reusable "modules" comprised of models, views and controllers.
//Bar chart module code
org.example.chart.bar.module
org.example.chart.bar.model
org.example.chart.bar.view
org.example.chart.bar.controller
I also have a big dataSource singleton and a dataManager for loading data to the dataSource:
org.example.data.dataSource
org.example.data.dataManager //populates the dataSource with CSV data
And finally translation strings and settings that should be available across the app:
org.example.static.translations
org.example.static.settings
How would you (re-)organize this so that I have easy access to the application level singletons (such as dataSource, dataManager, translations etc.) and can easily instantiate reusable modules that get scoped under the current application instance?
(Would you for example, already from the beginning, use the same namespace for your "classes" and your app? Or would you perhaps make references like so: myApp.translations = org.example.static.translations?)
No we don't namespace. We write modular code and we use module loaders.
Example of a module loader would be require.js or browserify or seajs.
And an example module would be something like:
(function () {
var jQuery = require("jQuery");
var chart = require("chart");
...
define("moduleName", moduleObject);
})();
There is nothing stopping you adding another name to the class. For example.
org.ds = org.example.data.dataSource;
then you can call
org.ds.getDatasource();
instead of
org.example.data.dataSource.getDatasource();
but both will still work.
EDIT: You could also create other simpler functions that call it taking it out of the oo structure
var dataSource = function () { return org.example.data.dataSource.getDatasource(); };
Consider using something like RequireJS for organizing your modules.
Some excellent resources from Addy Osmani :
http://addyosmani.com/largescalejavascript/
http://addyosmani.com/blog/large-scale-jquery/
http://addyosmani.com/resources/essentialjsdesignpatterns/book/