Restangular - Specific Function to Specific Factory - angularjs

I'm working with restangular and so far, everything works very well, but, i have this issue that cannot resolve.
I define a abstract repository with the basic operations like this:
app.factory('AbstractRepository', [
function(){
function AbstractRepository(restangular, route) {
this.restangular = restangular;
this.route = route;
};
AbstractRepository.prototype = {
getList: function (params) {
return this.restangular.all(this.route).getList(params).$object;
},
get: function (id) {
return this.restangular.one(this.route, id).get();
},
getView: function (id) {
return this.restangular.one(this.route, id).one(this.route + 'view').get();
},
update: function (updatedResource) {
return updatedResource.put().$object;
},
create: function (newResource) {
return this.restangular.all(this.route).post(newResource);
},
remove: function (object) {
return this.restangular.one(this.route, object.id).remove();
},
};
AbstractRepository.extend = function (repository) {
repository.prototype = Object.create(AbstractRepository.prototype);
repository.prototype.constructor = repository;
}
return AbstractRepository;
}
]);
And the specific respository:
app.factory('ServiceRepository', ['Restangular', 'AbstractRepository',
function (restangular, AbstractRepository) {
function ServiceRepository() {
//restangular.setBaseUrl("http://192.168.0.144:8080/api/rest/services/");
AbstractRepository.call(this, restangular,'http://192.168.0.144:8080/api/rest/services/');
}
AbstractRepository.extend(ServiceRepository);
return new ServiceRepository();
}
And i call the methods :
ServiceRepository.getList();
And now i want to implement and function (getServicesByOperatorId) that only works in the specific repository, not in the abstract. So i can call it like this:
ServiceRepository.getServicesByOperatorId({"operatorId":7});
If i define the function in the prototype of the abstract it works, but i want i way to define in the specific.
Thank you very much for your time.

Finally i find a way to do what i want:
In the specific factory define the prototype and extend from the abstract prototype like this:
ServiceRepository.prototype = {
getServicesByOperatorId: function (id) {
return this.restangular.all(this.route + 'getServicesByOperatorId').getList(id).$object;
}
}
angular.extend(ServiceRepository.prototype, AbstractRepository.prototype);
And in the abstract repository, remove this definition:
AbstractRepository.extend = function (repository) {
repository.prototype = Object.create(AbstractRepository.prototype);
repository.prototype.constructor = repository;
}
No i can access to de getList() method and the getServicesByOperatorId().

Related

angularjs unit testing unable to inject a service

I have a really simple service:
'use strict';
angular.module('sapphire.orders').service('deliveryDatesService', service);
function service() {
return {
clearAddressReason: clearAddressReason,
getMinDate: getMinDate
};
//////////////////////////////////////////////////
function clearAddressReason(model, dateReasons, reasons) {
if (model.manualAddress) {
model.overrideDates = true;
reasons.date = dateReasons.filter(function (item) {
return item.value === 'D4';
})[0];
} else {
reasons.address = null;
if (!reasons.date || reasons.date.value === 'D4') {
reasons.date = null;
model.overrideDates = false;
}
}
};
function getMinDate(model) {
var now = new Date();
// If we are not overriding dates, set to today
if (!model.overrideDates) return now;
// If dates are overriden, then the min date is today + daysToDispatch
return new Date(now.setDate(now.getDate() + model.daysToDispatch));
};
};
It has no dependencies, so I want to test the methods.
So I have tried to create a spec like this:
'use strict';
describe('Service: deliveryDatesService', function () {
beforeEach(module('sapphire.orders'));
var service,
reasons,
dateReasons;
beforeEach(inject(function (deliveryDatesService) {
console.log(deliveryDatesService);
service = deliveryDatesService;
reasons = {};
dateReasons = [{ value: 'D4' }];
}));
it('can create an instance of the service', function () {
expect(service).toBeDefined();
});
it('if manual delivery address is true, then override dates should be true', function () {
var model = { manualDeliveryDate: true };
service.clearAddressReason(model, dateReasons, reasons);
expect(model.overrideDates).toBe(true);
});
it('if manual delivery address is false, then override dates should be false', function () {
var model = { manualDeliveryDate: false };
service.clearAddressReason(model, dateReasons, reasons);
expect(model.overrideDates).toBe(false);
});
it('minimum date cannot be less than today', function () {
var model = { };
var minDate = service.getMinDate(model);
var now = new Date();
expect(minDate).toBeGreaterThan(now);
});
});
But my service is always undefined. Can someone tell me what I am doing wrong please?
Update
So, it turns out this is to do with one or more services interfering somehow.
In my karma.conf.js I had declared all my bower applications and then this:
'src/app/app.module.js',
'src/app/**/*module.js',
'src/app/**/*constants.js',
'src/app/**/*service.js',
'src/app/**/*routes.js',
'src/app/**/*.js',
'test/spec/**/*.js'
I created a test service in the root of my scripts directory and then created a spec file to see if it was created. It moaned at me about a reference error in a file that was not related at all. It moaned about this bit of code:
angular.module('sapphire.core').factory('options', service);
function service($rootScope) {
return {
get: get,
save: save
};
//////////////////////////////////////////////////
function get() {
if (Modernizr.localstorage) {
var storageData = angular.fromJson(localStorage.options);
if (storageData) {
return angular.fromJson(storageData);
}
}
return {
background: {
enabled: true,
enableSnow: true,
opacity: 0.6
}
};
};
function save(options) {
if (Modernizr.localstorage) {
localStorage.options = angular.toJson(options);
$rootScope.$options = get();
}
};
};
stating that Modernizr is not defined.
I changed the code to this:
angular.module('sapphire.core').factory('options', service);
function service($rootScope) {
return {
get: get,
save: save
};
//////////////////////////////////////////////////
function get() {
if (typeof Modernizr == 'object' && Modernizr.localstorage) {
var storageData = angular.fromJson(localStorage.options);
if (storageData) {
return angular.fromJson(storageData);
}
}
return {
background: {
enabled: true,
enableSnow: true,
opacity: 0.6
}
};
};
function save(options) {
if (typeof Modernizr == 'object' && Modernizr.localstorage) {
localStorage.options = angular.toJson(options);
$rootScope.$options = get();
}
};
};
and it started working. But my other test was not.
So I changed my references in karma.conf.js to this:
'src/app/app.module.js',
'src/app/orders/orders.module.js',
'src/app/orders/shared/*.js',
'test/spec/**/*.js'
and it started working.
That leads me to believe there is something wrong with my application somewhere. Maybe another reference like Modernizr. I still have an outstanding question though. How can services that are not dependant on another service interfere?
I think it's worth noting that each service, controller, directive is in it's own file and they all follow this structure:
(function () {
'use strict';
angular.module('sapphire.core').factory('options', service);
function service($rootScope) {
return {
get: get,
save: save
};
//////////////////////////////////////////////////
function get() {
if (typeof Modernizr == 'object' && Modernizr.localstorage) {
var storageData = angular.fromJson(localStorage.options);
if (storageData) {
return angular.fromJson(storageData);
}
}
return {
background: {
enabled: true,
enableSnow: true,
opacity: 0.6
}
};
};
function save(options) {
if (typeof Modernizr == 'object' && Modernizr.localstorage) {
localStorage.options = angular.toJson(options);
$rootScope.$options = get();
}
};
};
})();
I am wondering that because I wrap them in anonymous functions that execute themselves, is that what is causing this problem?
* Solution *
So in the end I found out exactly what was causing this issue. It was indeed to do with the file in karma.conf.js. I had told it to load all files and somewhere in there was something it didn't like.
After a bit of playing I finally found what it was and thought I would share it just in case someone else gets here.
The issue was routes. I am using ui.router and it appears that having them in your tests fail.
I changed my files section to this:
'src/app/app.module.js',
'src/app/**/*module.js',
'src/app/**/*constants.js',
'src/app/**/*service.js',
'src/app/**/*controller.js',
//'src/app/**/*routes.js',
'test/spec/**/*.js'
As you can see I have a routes file(s) commented out. If I bring them back in, everything fails.
I think your inner deliveryDatesService variable is hiding the external one.
To avoid that, you can try puting underscores around the inner service variable as per the spec on the website below:
https://docs.angularjs.org/api/ngMock/function/angular.mock.inject
Look for
'Resolving References (Underscore Wrapping)'
on that page.
Then your code would look like this:
beforeEach(inject(function (_deliveryDatesService_) {
console.log(_deliveryDatesService_);
service = _deliveryDatesService_;
reasons = {};
dateReasons = [{ value: 'D4' }];
}));
Also you need to make sure that all required files and directories are declared in karma.conf.js so that the test framework can use them.

Typeahead search function

So I am using the typeahead directive that's part of Angular UI project. I have a function that calls a factory (using $resource to call an api). The first function didn't work but the second one did. What's happening differently here? I assumed these would produce the exact same result but apparently I am wrong:
// this didn't work, it doesn't display a list of items in typeahead, no errors.
$scope.getLocation = function(val) {
return LocationService.search({ term: val }, function (res) {
return res.data.map(function (item) {
return item;
});
});
};
// this worked
$scope.getLocation = function(val) {
return LocationService.search({ term: val }).$promise.then(function (res){
return res.data.map(function (item) {
return item;
});
});
};
Do you have $resource wrapped in LocationService? Something like:
function LocationService($resource) {
return {
search : function(query){
var locationResource = $resource('url/',
{},
{
search : {
method: 'POST',
responseType : 'json'
}
});
return locationResource.search({ q : query });
}
};
}
If so, in your first example, you're just passing a callback as second variable to LocationService, which isn't handled in the function definition. The returned function of $resource can take a callback as a second parameter, but not if you've wrapped it. If you wanted, you could pass the callback to the service itself, like:
function LocationService($resource) {
return {
search : function(query, cb){
var locationResource = $resource('url/',
{},
{
search : {
method: 'POST',
responseType : 'json'
}
});
return locationResource.search({ q : query }, cb);
}
};
}

AngularJS $http.get in MVC5 Areas

I have an Area named Employer in MVC5 and in the root a folder named app that contains a folder named list. In list folder I created js files and as a service factory I user this code :
angSalaryApp.factory('listService', ["$http",
function ($http) {
return {
newList: newList
};
function newList() {
return $http.get("Areas/Employer/List/newlist");
}
return {
userLists: userLists
};
function userLists() {
return $http.get("Areas/Employer/List/getlists");
}
}
]);
but newlist and userlists actions aren't called and my controller variables are undefined. it's my controller code :
angSalaryApp.controller('listController',
function ListController($scope, listService) {
$scope.list = listService.newList;
$scope.userlist = [];
$scope.count = 0;
$scope.submitForm = function () {
};
$scope.loadLists = function () {
$scope.userlist = listService.userLists;
$scope.d = "ffdgdfg";
};
$scope.updateName = function (newtitle) {
$scope.list.Name = newtitle;
};
});
You need to restructure your factory like this:
angSalaryApp.factory('listService', ["$http",
function ($http) {
return {
newList: newList,
userLists: userLists
};
function newList() {
return $http.get("Areas/Employer/List/newlist");
}
function userLists() {
return $http.get("Areas/Employer/List/getlists");
}
}
]);
...otherwise userLists will be private.
Have you tried to move the route initialization to the Area registration?
using System.Web.Mvc;
namespace MyAreaTest
{
public class MyAreaRegistration : AreaRegistration
{
public override string AreaName
{
get { return "MyArea"; }
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Area_default",
"MyArea/{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
}

AngularJS Function within Function

I have a notifications dropdown in my AngularJS application. I want to call a function within the function that open the dropdown. Here is what I mean:
$scope.showNotif = false;
$scope.toggleNotifDropdown = function(event) {
$scope.showNotif = !$scope.showNotif;
readNotifications = function() {
NotificationService.readNotifs().then(
function(success) {
console.log("Notifications read!");
},
function(errors) {
console.log("Something wrong happened.");
}
);
};
if($scope.showNotif) {
$document.bind('click', $scope.globalNotifClose);
} else {
$document.unbind('click', $scope.globalNotifClose);
}
event.stopPropagation();
};
The notifications dropdown works perfectly, I just can't get that function readNotifications() to work for me. Any suggestions would be great! Thanks!
There is no point in declaring the function within your scope function and you never call it either. Declare it outside and call it from inside
$scope.toggleNotifDropdown = function (event) {
$scope.showNotif = !$scope.showNotif;
// call the function declared below
readNotifications();
if ($scope.showNotif) {
$document.bind('click', $scope.globalNotifClose);
} else {
$document.unbind('click', $scope.globalNotifClose);
}
event.stopPropagation();
};
// function declaration
var readNotifications = function () {
NotificationService.readNotifs().then(
function (success) {
console.log("Notifications read!");
},
function (errors) {
console.log("Something wrong happened.");
});
};

How can i add more then one functions in a service or factory?

i must write a Service who has a lot of functions inside. This i must inject into a controller.
But, then i write a factory with 3 or more functions, angular found the first one, all others are undefined. - Why?
mySystem.factory('testFactory', function($http) {
return {
checkDates: function() {
myData.VDate = myData.VDate.format('dd.MM.yyyy');
}
return {
checkrequiered: function() {
var check = true;
if (myData.locId.lenght === 0) {
check=false;
}
return check;
}
return {
updateData: function() {
'...'
}
});
Whats wrong?
What's wrong is that you have three return statements, which means all but the first will be ignored. Return all the functions in a single object:
return {
checkDates: function() {
myData.VDate = myData.VDate.format('dd.MM.yyyy');
},
checkRequired: function() {
return (myData.locId.length !== 0);
},
updateData: function() {
'...'
}
};

Resources