Angularjs : How to switch between different implementations of a provider using DI - angularjs

First I'd like to say my appreciation for this great website that I rely on rather often but never have used to ask anything.
I'm currently learning AngularJS by reading "Mastering web application development with AngularJS" and going through the provided examples.
I would like to understand how I can switch between different implementations of a provider (service) with minimal code change.
Here is the "notifications" service that I need to configure with different implementations of an "archiver" service, code for both below :
angular.module('notificationsApp', ['archiver'])
.provider('notificationsService', function () {
var notifications = [];
return {
$get : function(archiverService) {
return {
push:function (notification) {
var notificationToArchive;
var newLen = notifications.unshift(notification);
if (newLen > 5) {
notificationToArchive = notifications.pop();
archiverService.archive(notificationToArchive);
}
}
};
}
};
})
.config(function(notificationsServiceProvider){
**How could I make the 'archiverService' be of superConsoleArchiverService or consoleArchiverService ?**
});
I would like to be able to choose between different implementations for my "archiverService", namely "superConsoleArchiverService" or "consoleArchiverService" as defined in the following module.
angular.module('archiver', [])
.provider('consoleArchiverService', function () {
return {
$get : function() {
return {
archive:function (archivedNotification) {
console.log(archivedNotification);
}
};
}
};
})
.provider('superConsoleArchiverService', function () {
return {
$get : function() {
return {
archive:function (archivedNotification) {
console.log('super ' + archivedNotification);
}
};
}
};
});
Thanks a lot for helping me through this !
(also, I hope this question makes sense and has not been answered a gazillion times)

Let's say you have some condition, say a variable to use_super.
Then you could do something like this, by injecting $provide, and both of your providers:
$provide.value('archiver', use_super ? superConsoleArchiverService : consoleArchiverService);
Hope this helped!

Thanks to the answer provided by hassassin I was able to make it work, following is some working version, no code was changed in the 'archiver' module.
angular.module('notificationsApp', ['archiver'])
.provider('notificationsService', function () {
var notifications = [];
return {
$get : function(configuredArchiver) {
return {
push:function (notification) {
var notificationToArchive;
var newLen = notifications.unshift(notification);
if (newLen > 5) {
notificationToArchive = notifications.pop();
configuredArchiver.archive(notificationToArchive);
}
}
};
}
};
})
.config(function(notificationsServiceProvider, superConsoleArchiverServiceProvider, consoleArchiverServiceProvider, $provide){
// Here it is possible to set the 'configuredArchiver to either one of my archivers
//$provide.provider('configuredArchiver',consoleArchiverServiceProvider);
$provide.provider('configuredArchiver',superConsoleArchiverServiceProvider);
});
Some things I still don't fully understand like why can't I inject the 'configuredArchiver' directly in the 'notificationService' provider, but I strongly suspect it is related to my still very small grasp on the life cycle of AngularJS objects. Back to reading !

Related

"_mock.default.unsubscribe is not a function" React using mock.js can't unsubscribe

Will attach full example of this task mock.js unsubscribe. I have a store reducer (tbodyCommand.reducer.js) that needs to take 10 rows from data by subscribing it and than after that it have to be unsubscribed. I am using standard .unsubscribe() method, but it goes to TypeError _mock.default.unsubscribe is not a function. How can i turn off my subscribing?
I found similar question over here unsubscribe is not a function on an observable where main problem appears in RxJS version but mine seems to work good for me. The example of syntax construction over here with subscribe method doesn't contain any way to deal with mock.js; How can I use it in my example so it will work? Is there any other ways to come to this problem simpler?
method
Got changed properties$.subscribe to variable and took out return from subscribe function.
var row_temp = [];
const makeTbodyArray = () => {
properties$.subscribe((data) => { // this stuff need to go in variable
if (row_temp.length < 11) {
row_temp.push(data);
} else {
properties$.unsubscribe();
return row_temp; // taking out return
}
});
};
to
var row_temp = [];
const makeTbodyArray = () => {
let subscription = properties$.subscribe((data) => {
if (row_temp.length < 11) {
row_temp.push(data);
} else {
subscription.unsubscribe();
}
});
return row_temp; // like that
};

Jasmine: Testing a function returning a promise

I am writing tests using Jasmine for my angular application. All the tests are passing. My class looks like follows:
class xyz implements ng.IComponentController {
private myList: ng.IPromise<MyList[]> ;
//declare necessary variables
/* #ngInject */
constructor(private ListService: ListService,
) {
this.myList = this.ListService.getList();
}
public onChange(): void {
this.isNameUnique(this.name).then(function(unique){
scope.isUnique = unique;
scope.errorNameInput = !reg.test(scope.name) || !scope.isUnique;
scope.myFunction({
//do something
});
});
}
public isNameUnique(name: string): ng.IPromise<boolean> {
return this.myList
.then(
(names) => {
_.mapValues(names, function(name){
return name.uuid.toLowerCase();
});
return (_.findIndex(names, { uuid : uuid.toLowerCase() }) === -1) ? true : false;
});
}
}
Here, I am using ListService to pre-populate my list in the constructor itself (so it calls the service only once). Then, in my onChange method, I am checking
if a name is unique or not. The isNameUnique is returning a boolean promise.
Now, I'm trying to get 100% coverage for my test. I'm getting confused about testing isNameUnique method here. My first test is:
(Assuming myList is a json similar to response I will get from service)
this.$scope.myFunction = jasmine.createSpy('myFunction');
it('should ...', function() {
this.view.find(NAME_INPUT).val('blue').change(); // my view element.
this.getList.resolve(myList);
this.controller.isNameUnique('blue').then(function (unique) {
expect(unique).toEqual(false); //since blue is already in my json
expect(this.controller.errorNameInput).toEqual(true); //since its not unique, errornameinput will be set to true
expect(this.$scope.myFunction).toHaveBeenCalled();
});
});
I would expect this test to cover the line: scope.errorNameInput = !reg.test(scope.name) || !scope.isUnique and invocation of myFunction() but it still shows uncovered. Not sure why.
Please let me know if you see anything else wrong since I'm quite new to Angular and Jasmine. Thanks.
You need to call $scope.$digest() to cause your promise to resolve in your test. There is a handy tutorial that discusses this in depth here
Hope that helps!

how to make a relations between angular 1 components?

i'm looking for some solution to work with angular 1.5 components in a way that a change in one component will change something in other component in another place in the tree.
what so you think is the best solution?
rootScope?
redux?
events?
some other global variable?
If you want your own service without redux or anything else, you can implement a service like :
angular.module("my_service", [])
.factory("Message", function() {
var messages = {
// m1: [],
// m2: []
};
function receive(message, messageHandler) {
if (!Array.isArray(messages[message])) {
messages[message] = [];
}
messages[message].push(messageHandler);
}
function send(messageName, message) {
if (Array.isArray(messages[messageName])) {
messages[messageName].forEach(function(messageHandler) {
messageHandler(message);
});
} else {
console.warn("sent message", message, "is not in the message list...");
}
}
return {
send: send,
receive: receive
};
}
});
And in somewhere else you can create two controllers for example :
angular.module("app", ["my_service"])
.controller("app1", function(Message) {
Message.receive("sthHappened", function(whatHappened) {
console.log("app1 says :", whatHappened);
});
})
.controller("app2", function(Message) {
Message.send("sthHappened", "app2 initiated");
});
Basically you register a function in order to execute when a message received, and a trigger when something happened in order to execute the registered functions.
Of course you may need to make some additions according to what you need and improve performance or async op in order to break the sequential execution, but this the basic structure of a publisher-subscriber mechanism in order to create a messaging channel between your controllers or directives or any functions.

Angular-Leaflet heatmap data update

I'm using the Angular-Leaflet directive to display a heatmap, and I want the data to evolve through time. My code looks like:
getData().then(function (data) {
$scope.heat.index = 0;
$scope.heat.data = data;
$scope.layers.overlays.heat = {
name: "Heatmap",
type: "heat",
data: $scope.heat.data[$scope.heat.index], // data is a dictionary, not an array
visible: true
};
$scope.$watch('heat.index', function (new_index) {
$scope.layers.overlays.heat.data = $scope.heat.data[new_index];
});
});
However, when I change data.index (through a slider), nothing happens. What could be going on? I know that Angular-Leaflet supports this behavior because of this Github issue where someone added it.
leafletData.getMap().then(function (map) {
map.eachLayer(function (layer) {
if (layer.options.layerName == 'heatmap') {
layer.setLatLngs(newHeatmapData);
}
})
})
This worked for me.
Leaflet.heat provides a redraw() method but that didn't work for me.

Creating a custom Angular filter with TypeScript

I'm trying to work out the best way of creating a custom Angular Filter with TypeScript.
All the code samples I see use something like:
myModule.filter( "myFilter", function()
{
return function( input )
{
// filter stuff here
return result;
}
}
... which works, but seems messy as I want to keep all my filter code separate. So I want to know how to declare the filter as a separate file (eg filters/reverse-filter.ts) so I can create it like:
myModule.filter( "filterName", moduleName.myFilter );
... the same way you would for Controllers, Services etc.
The documentation for TS and Angular together seems pretty thin on the ground, especially where filters are concerned - can anyone help out?
Cheers!
Functions can be exported from modules like this:
module moduleName {
export function myFilter()
{
return function(input)
{
// filter stuff here
return result;
}
}
}
then outside the module:
myModule.filter("filterName", moduleName.myFilter);
Then it would then be possible to do things like automatically register all of the filters defined in the moduleName module by iterating over its public properties.
Maybe too late but can be useful for someone else.
module dashboard.filters{
export class TrustResource{
static $inject:string[] = ['$sce'];
static filter($sce:ng.ISCEService){
return (value)=> {
return $sce.trustAsResourceUrl(value)
};
}
}
}
dashboard.Bootstrap.angular.filter('trustAsResourceUrl',dashboard.filters.TrustResource.filter);
To explain the last line:
dashboard.Bootstrap.angular.filter('trustAsResourceUrl',dashboard.filters.TrustResource.filter);)
I will add a piece of code, wich represents my Bootstrap class, so you can understand it.
module dashboard {
export class Bootstrap {
static angular:ng.IModule;
static start(){
Bootstrap.angular = angular.module('EmbApp', dashboard.Bootstrap.$inject);
}
}
}
//run application
dashboard.Bootstrap.start();
If you need more information about how it works, you can checkout my own TypeScript/AngularJS/Less structure here
Here's an example using the injector to get dependencies into your filter. This one gets injected with the $filter service.
export class CustomDateFilter {
public static Factory() {
var factoryFunction = ($filter: ng.IFilterService) => {
var angularDateFilter = $filter('date');
return (theDate: string) => {
return angularDateFilter(theDate, "yyyy MM dd - hh:mm a");
};
};
factoryFunction.$inject = ['$filter'];
return factoryFunction;
}
}
// and in the bootstrap code:
app.filter('customDate', your.module.CustomDateFilter.Factory());
You should use something like this to inject dependencies
myModule.filter('filterName', ['$http', moduleName.myFilter]);
You can create a filter using class with a static function.
export class FilterClass
{
static id = "FilterId"; //FilterName, use while consume
/*#ngInject*/
public static instance() { //static instance function
let dataFilter = () => {
let filteredObject = () => {
//filter logic
return filteredData;
}
return filteredObject;
}
return dataFilter;
}
}
//Module configuration
angular.module(myModule).filter(FilterClass.id, FilterClass.instance());
Consume this filter in the controller using below way.
let FilterFun:any = this.$filter('FilterId');
let Filteroutput = FilterFun();

Resources