How can I extend the $http service in angular? - angularjs

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>

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 Factory, Service, and Controller not Working Together with $Resource

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!

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.

Mockjax use in an angular app

I am new to AngularJS.
Can I use mockjax in AngularJS by calling mock end points using $http services method get/post.
If not is there a way $http provides a way to create end points and call them?
For example
The MockService is something like this
$.mockjax({
url: '/sometest/abc',
type: 'post',
responseTime: 2000,
responseText: {
LoginSuccessful: true,
ErrorMessage: "Login Successfuly",
Token: "P{FsGAgtZT7T"
}
});
And the DataService that I have created has is as below.
'use strict';
//Data service
angular.module('app').factory('dataService',['$http', function($http){
var restCall = function (url, type, data, successCallback, errorCallback) {
$http({
method: type,
url: url,
data: data,
}).
success(function (data, status, headers, config) {
successCallback(data);
}).
error(function (data, status, headers, config) {
errorCallback(data)
});
};
return {
getTemplate: function (success, error) {
restCall('/sometest/abc', 'GET', null, success, error);
}
};
}]);
And the controller is below as
angular.module('App').controller('mainCtrl', ['$scope', 'trackService', 'dataService',
function ($scope, TrackService, ds) {
ds.getTemplate(function (data) {
//do some calculation
}, function () {
console.warn('Something is not right');
});}]);
I want know that is this the correct way of using the $http, or something else should be done.This is something I am trying to achieve in the actual code, but not in Unit test using jasmine.
I had to do this recently and I found that using Angular's built-in stuff (angular-mocks) was pretty easy.
Here's the basic idea: you include a separate module in your test harness which mocks out your requests, it's a basic URL matcher with either strings or regular expressions...
// in some other file that you include only for tests...
var myTestApp = angular.module('myApp', ['myApp']);
myTestApp
.config(function ($provide, $httpProvider) {
// Here we tell Angular that instead of the "real" $httpBackend, we want it
// to use our mock backend
$provide.decorator('$httpBackend', angular.mock.e2e.$httpBackendDecorator);
})
.run(function ($httpBackend) {
$httpBackend.whenGET('/some/url')
.respond(function (method, url, requestData, headers) {
// Do whatever checks you need to on the data, headers, etc
// Then return whatever you need to for this test.
// Perhaps we want to test the failure scenario...
return [
200, // Status code
{ firstname: 'Jordan' }, // Response body
{} // Response headers
];
});
});
Check the first link above to read my blog post on this.

AngularJS - scope variable does not get updated from method

I'm totally new to AngularJs and I have this problem I do not understand. I have two methods. The first one takes some data from a webservice and puts in in a variable defined in the scope. But when I want to use that variable in the second method it is undefined. Can someone help me understand why this is happening and provide a solution?
var myApp= angular.module( "myApp", [] );
myApp.controller("myAppController",
function( $scope ) {
$scope.getAll = function(){
$.ajax({
type: "GET",
dataType: "jsonp",
contentType: "application/json; charset=utf-8",
url: ..something...,
success: function (parameters) {
$scope.profiles = angular.copy(parameters); <-- correct data is returned
$scope.$apply();
},
error: function () {
alert("Error calling the web service.");
}
});
}
$scope.getCategories = function(){
var all = $scope.profiles; <-- At this point profiles is empty
...
}
$scope.getAll();
$scope.getCategories();
}
Use the $http service and promises:
$scope.profiles = $http.jsonp(url).then(function(r){ return r.data; });
$scope.categories = $scope.profiles.then(function(profiles) {
var params = { }; // build url params
return $http.jsonp(url, { params: params }).then(function(r){ return r.data; });
});
When you call getCategories(), getAll() hasn't finished yet, which is why profiles is empty. There are several ways to solve this. The best way would be to use promises the built-in $http service.
If you prefer to use jQuery, you can add a watcher on the profiles variable and only when it's populated run the getCategories().
Something like this should work:
$scope.getAll = function(){
$.ajax({
type: "GET",
dataType: "jsonp",
contentType: "application/json; charset=utf-8",
url: ..something...,
success: function (parameters) {
$scope.profiles = angular.copy(parameters); <-- correct data is returned
$scope.$apply();
},
error: function () {
alert("Error calling the web service.");
}
});
}
$scope.getCategories = function(){
var all = $scope.profiles;
}
// Wait for the profiles to be loaded
$scope.watch('profiles', function() {
$scope.getCategories();
}
$scope.getAll();
There is no guarantee that getAll has completed before getCategories is invoked, since it is an asynchronous request. So if you want to sequentially invoke getAll and getCategories, you should invoke getCategories inside the success callback of getAll. You could also look into promises for a neater way of chaining asynchronous callbacks (I assume you're using jQuery since you're calling $.ajax).
...
<snipped some code>
success: function(parameters) {
// snipped more code
$scope.getCategories();
}
(and if you're using jQuery promises)
$.ajax(ajaxCallOneOpts).then($.ajax(ajaxCallTwoOpts));
Neither are very "Angularish" though, so you might want to look into some of the provided services for working with http/rest resources instead of using jQuery.
Why are you using a jQuery ajax request in angular? If you write jQuery style code and wrap it angular, you're going to have a bad time...
Here is an angularised version:
myApp.controller("myAppController",
function( $scope, $q, $http ) {
$scope.getAll = function(){
var deferred = $q.defer();
$scope.profiles = deferred.promise;
$http.jsonp('your url').then(function(data) {
deferred.resolve(data);
});
});
$scope.getCategories = function(){
$q.when($scope.profiles).then(function(profiles) {
... <-- At this point profiles is populated
});
}
$scope.getAll();
$scope.getCategories();
}

Resources