Angular Factory, Service, and Controller not Working Together with $Resource - angularjs

I have an Angular service that's just a setter and getter for a variable I need to pass between controllers:
var MyApp = angular.module('MyApp', ['ngResource', 'ngRoute'])
.service('userToken', function() {
var userTokenProp;
return {
getter: function() {
return userTokenProp;
},
setter: function(value) {
userTokenProp = value;
}
};
})
Then a bit further down I'm trying to use $resource in a factory based on the value of that token. There's another almost identical factory that is able to set the token, but this latter factory can't get or use the token. I also need to send the token into the header while making the request. The code I have for that is this, a form I settled on after trying any number of other ways to pass the data into the factory and to set the header in $resource:
.factory("apiQueryProfileFactory", function($resource, userToken) {
console.log("token: " + userToken.getter());
var uToken = userToken.getter();
var resource = $resource('http://localhost:3001/lsportal/api/v1/profile/:path', {}, {
get: {
method: "GET",
headers: {'token': uToken}
},
});
return resource;
});
And then from the controller (which, yes, is passed the factory and service):
apiQueryProfileFactory.get({path:'role'}, function(data) {
})
Just don't get what I'm missing, and I've been over and over it for a while now!

Related

How to retrieve data faster from angularJS get call

I'm calling angularJS get call and parse the response and showing the response in UI, but I see a slight delay on the UI, is there a way that I can fix this:
Here is what my code is :
controller.js:
function controller($scope, empDetails) {
var empName;
empDetails.getEmpDetails().then(function successCallback(response) {
empName= response.data.name;
if (empName) {
$scope.name= empName;
}
});
angular.module('abc')
.controller('controller', controller);
})();
service.js:
(function () {
"use strict";
var empDetails= function ($http) {
var factory = {};
factory.getEmpDetails = function () {
return $http({
method: 'GET',
url: '/someurl'
}).then(function (data) {
return data;
})
}
return factory;
};
empDetails.inject = ['$http'];
angular.module('abc').service('empDetails', empDetails);
}())
thanks
Obviously, it will take time to load data on the screen because you are calling an API.
But you can achieve this by,
Resolving your routes. //A resolve contains one or more promises that must resolve successfully before the route will change. This means you can wait for data to become available before showing a view.
Fetch only those fields which are necessary on front-end. //this will make the lightweight API.
Below are the links for using resolve in routeProvider/stateProvider:
https://docs.angularjs.org/api/ngRoute/provider/$routeProvider
https://odetocode.com/blogs/scott/archive/2014/05/20/using-resolve-in-angularjs-routes.aspx

Angular JS saving information from factory AJAX request

I have a factory where I'm getting data from a server using the $http methods:
.factory('$factory', function ($q, $http, $timeout, $state, $ionicHistory, $localstorage) {
var obj = [];
var functions = {
getData: function () {
var dfd = $q.defer();
if(!obj){
$http({
url: remoteUrl+'/getdata',
method: 'POST',
data: {}
}).then(function(response) {
$timeout(function(){
obj = response.data;
dfd.resolve(response.data);
}, 2000)
}, function(response) {
}
}else{
return obj;
}
return dfd.promise;
}
}
}
So this gets the data and puts it in an object. Correct me if I'm wrong, but this method of using a factory for this type of action is so it's not tied to a controller, and can be used anywhere in my application.
With this in mind, I wish to make it so I can get the data anywhere in my app without having to query the server each time. i.e. once it's queried the server the factory saves the response to an object (like I'm doing now). But I'm having trouble accessing the data afterwards in another controller for example.
I've started to make what I think it should look like by using the if(!obj) line, but in the else statement I cant seem to just return the obj object. It throws errors as it's not returning a promise like it's expected too.
I'm not sure if I'm even along the right lines with this.
Thanks
You're returning a promise so you need to always return a promise even on the cached response, you can do it relatively easy at this level by wrapping it in a $q.when (https://docs.angularjs.org/api/ng/service/$q) which will return a promise immediately resolved.
return $q.when(obj);
Although $http service has built in caching, so you may want to take a look under the Cache section.
https://docs.angularjs.org/api/ng/service/$http
https://www.ng-book.com/p/Caching/
This should work:
Insteada assigning obj = [] assign as a null. Usually I prefer callback. You can try this code:
.factory('$factory', function($q, $http, $timeout, $state, $ionicHistory, $localstorage) {
var obj = [];
var functions = {
getData: function(cb) {
// instead of checking !obj you have to check for length or you have to set obj as null
if (obj && obj.length == 0) {
$http({
url: remoteUrl + '/getdata',
method: 'POST',
data: {}
}).then(function(response) {
obj = response.data;
cb(response.data)
}, function(response) {
}
}
else {
cb(obj)
}
}
}
}
})
// You can use callback by following code
$factory.getData(function(response){
// response will come here
})

How can I extend the $http service in angular?

Unfortunately, we're stuck running 1.2.26 (will upgrade to 1.2.28 when it's gemified).
In the meantime, how can I patch (heh) $http so that the short-hand patch method is available? I'm pretty new to the whole service/factory/module thing. I've done hours of searching and can't seem to figure it out.
myApp.factory('patchedHTTP', function($http, BasicService) {
// $http['patch'] = function(url, data, config) {
// return $http(angular.extend(config || {}, {
// method: 'patch',
// url: url,
// data: data
// }));
// };
var extended = angular.extend(BasicService, {});
extended.createShortMethodsWithData('patch');
return extended;
});
Above is the best I've got... and it doesn't do anything XD
You can do this with an angular decorator.
A service decorator intercepts the creation of a service, allowing it to override or modify the behaviour of the service. The object returned by the decorator may be the original service, or a new service object which replaces or wraps and delegates to the original service.
For more information you can check angular documentation.
Example:
var app = angular.module('app');
app.decorator('$http', function ($delegate) {
// NOTE: $delegate is the original service
$delegate.patch = function () {
// do the implementation here
};
return $delegate;
});
// usage
app.controller('SomeController', function($http) {
$http.patch();
});
You can keep this decorator until you upgrade to some newer version and than just safely delete it.
The module.decorator has been added to the module API in version 1.4. That's why it is not working in 1.2.x.
Please find below a working demo or here at jsfiddle.
It took me a while to implement the patch method because I've missed to return the promise of $http. But now it should be working.
angular.module('patchDemo', [])
.config(function ($provide) {
$provide.decorator('$http', function ($delegate) {
// NOTE: $delegate is the original service
$delegate.patch = function(url, data, config) {
var paramsObj = angular.extend({}, config || {}, {
method: 'PATCH',
url: url,
data: data
});
return $delegate(paramsObj);
}
return $delegate;
});
})
.controller('MainController', MainController);
function MainController($http) {
console.log($http.patch);
//$http({method: 'PATCH', url: 'http://jsonplaceholder.typicode.com/posts/1', data: {title:'foo'}}); //>>>>>working long version of patch
$http.patch('http://jsonplaceholder.typicode.com/posts/1', {
title: 'foo'
}).then(function(response) {
console.log(response);
});
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.26/angular.js"></script>
<div ng-app="patchDemo" ng-controller="MainController"></div>

Uncaught Error when using Angular Factory to provide API calls

I am trying to build a service to give my application full access to every single API resource available from a single service. Right now I have created my main Angular app module and an API service called APIService. The service has a factory that returns a number of accessible Angular $resource's to the different APIs. Here is the code.
var app = angular.module('MYAPP', ['ngRoute', 'ngSanitize', 'ngResource', 'apiService']);
var APIService = angular.module("apiService", ["ngResource"]);
APIService.factory("API", function ($resource) {
var apiFactory = {};
apiFactory.Alerts = $resource('/WebApi/Alert/:type/:id', {id:'all'},
{
systemUpdate: { method: 'GET' },
autoArchive: { method: 'POST', url: '/WebApi/Alert/Template/:type' }
});
return apiFactory;
});
However, when I try to load the page, I get an Uncaught Error within the angular.js file. Am I doing something wrong here?
You've forgotten to define a function inside your object:
apiFactory.Alerts = function(){
return $resource('/WebApi/Alert/:type/:id', {id:'all'},
{
systemUpdate: { method: 'GET' },
autoArchive: { method: 'POST', url: '/WebApi/Alert/Template/:type' }
});
}

How can I execute some code asynchronously before methods in my service are called?

I am having some problems with executing some tasks before my service is initialised and it's methods used. Some context:
I am producing an application which uses REST to communicate with 2 different backend systems (the reason for this is that if our client upgrades in the future it will still work). These backend systems have slightly different paths for the same REST calls.
To know which calls to use I thought a solution might be to call one test endpoint which exists in one, but not the other, and depending on the response code received, set a variable which is the beginning of the URL. e.g. /rest/services/StartpointService/.
All the REST calls are in a single factory and I tried something like this:
angular.module('myApp.services', [])
.factory('myServices', [
'$http',
'$q',
function($http, $q) {
//test function, if success we are using 1 backend, if fails, we use the other
var loginTest = function() {
var deferred = $q.defer();
$http( {
method: 'POST',
url: '/um/login?um_no_redirect=true'
})
.success(function(data, status, headers, config) {
deferred.resolve(status);
})
.error(function(data, status, headers, config) {
deferred.reject(status);
});
return deferred.promise;
};
var url;
loginTest()
.then( function(response) { //if backend1
if(responseCode === 200) {
url = '/rest/services/etc/etc' //set the URL
}
},
function(errorCode) { //If backend2
if(errorCode === 404) {
url = '/LCConnector/rest/services/etc/etc';
}
});
var service = {
realCall : function() {
//use 'url' variable in the $http call
}
}
return service;
}]);
Obviously as the loginTest is asyncronous, the service is injected into my controller and is called before url is set.
I have looked into running in a config or run block when the app is first initialised but can't quite understand how to get the variable out.
If i can give anything further details, please let me know!
Regards,
Augier
If this check is required before the application is initialized you can manually bootstrap your application after the Ajax call. Inside of your fail() or done() call backs you can alter the module config to reflect the url you need. From there you can easily inject your config into any service that requires this url.
example on jsfiddle
<div ng-controller="MyCtrl">{{url}}</div>
//if you chanée this url to /echo/fail and url will now be url1
var urlToCheck = '/echo/json/';
var myApp = angular.module('myApp', []);
myApp.controller("MyCtrl", ["$scope", "config", function ($scope, config) {
$scope.url = config.url;
}]);
$.ajax({
url: urlToCheck
}).fail(function () {
myApp.constant('config', {
url: '/fail-url'
});
}).done(function () {
myApp.constant('config', {
url: '/done-url'
});
}).always(function () {
angular.bootstrap(document, ['myApp']);
});
You could take advantage of $routeProvider, which allows you to delay your controller instantiation until your promise has been resolved.
$routeProvider exposes a method called resolve for that purpose. See the AngularJS doc:
http://docs.angularjs.org/api/ngRoute.$routeProvider
Additional information in this excellent SO question:
AngularJS : Initialize service with asynchronous data
Highly recommended.

Resources