Angular $http service cache not working properly - javascript

I have a $http service in angular js which has a cache enable for it. When first time load the app service gets call and get cache. Now when i call the service anywhere from the same page the data comes from cache but when i change the page route and again call the service from another page the data come from server(i am just changing the route not refreshing the page)
Edit =>
Code was working !! On route the data also came from cache but it took more time as there are few other call as. It just took more time then i accepted the cache to respond .If i call same from any click event then it will take 2ms to 3ms
here is my service
commonServicesModule.service('getDetails', ['$http', 'urlc',
function($http, urlc) {
return {
consumer: {
profile: function(id) {
return $http({
url: urlc.details.consumer.profile,
cache: true,
params: {
'consumer_id': id,
'hello': id,
},
method: 'GET',
}).success(function(result) {
return result;
});
},
}
}
}
])
Call from controller :
start = new Date().getTime();
/*get user information */
getDetails.consumer.profile('1').then(function(results) {
console.log('time taken for request form listCtrl ' + (new Date().getTime() - start) + 'ms');
});
when i call this from anywhere else after route it take the same time.

Try moving the consumer object into the body of the function, and return a reference to it, like so:
commonServicesModule.service('getDetails', ['$http', 'urlc', function($http, urlc) {
var getConsumer = {
profile: function(id) {
return $http({
url: urlc.details.consumer.profile,
cache: true,
params: {
'consumer_id': id,
'hello': id,
},
method: 'GET',
}).success(function(result) {
return result;
});
}
};
return { consumer: getConsumer };
}]);

Related

Convert JS Post Ajax to AngularJS Post Factory

I am trying to convert an Ajax call with WSSE authentication to an AngularJS factory.
The method is Post.
The intended use of this is to access the Adobe Analytics Rest API and return data to be converted to JSON and then visualised with d3.js.
I am not familiar with the properties that can be used in an AngularJS $http post call and so not sure what is the correct way to do the WSSE auth, dataType, callback etc.
This is the original ajax code which came from a public github repo:
(function($) {
window.MarketingCloud = {
env: {},
wsse: new Wsse(),
/** Make the api request */
/* callback should follow standard jQuery request format:
* function callback(data)
*/
makeRequest: function (username, secret, method, params, endpoint, callback)
{
var headers = MarketingCloud.wsse.generateAuth(username, secret);
var url = 'https://'+endpoint+'/admin/1.4/rest/?method='+method;
$.ajax(url, {
type:'POST',
data: params,
complete: callback,
dataType: "text",
headers: {
'X-WSSE': headers['X-WSSE']
}
});
}
};
})(jQuery);
This is the current way the code is being used with pure JS:
MarketingCloud.makeRequest(username, secret, method, params, endpoint, function(response) {
data = JSON.parse(response.responseText);
});
I want to convert this to a factory and a controller respectively.
This is what I have done for the factory so far:
app.factory('mainFactory', ['$http', function($http) {
var wsse = new Wsse ();
return function(username, secret, method, params, endpoint) {
return $http({
method: 'POST',
url: 'https://' + endpoint + '/admin/1.4/rest/?method=' + method,
data: params,
headers: {
'X-WSSE': wsse.generateAuth(username, secret)['X-WSSE']
},
dataType: 'text',
});
};
}]);
And this is what I have for the controller:
app.controller('mainController', ['$scope', 'mainFactory', function($scope, mainFactory) {
mainFactory.success(function(data) {
$scope.data = data;
});
}]);
Currently I get an error saying mainFactory.success is not a function which I assume is because the factory isn't working yet.
I have resolved this question myself. The parameters I was passing to the first function in the factory were globally defined already and therefore getting over-written.
The first function is not required anyway.
Here is the factory code:
app.factory('mainFactory', ['$http', function($http) {
var wsse = new Wsse ();
return {
getAnalytics : function (){
$http({
method: 'POST',
url: 'https://' + endpoint + '/admin/1.4/rest/?method=' + method,
data: params,
headers: {
'X-WSSE': wsse.generateAuth(username, secret)['X-WSSE']
}
})
.success(function(data) {
return data;
})
.error(function(err) {
return err;
});
}
};
}]);
And here is the controller code:
app.controller('mainController', ['$scope', 'mainFactory', function($scope, mainFactory) {
$scope.title = "Inn Site";
$scope.data = mainFactory.getAnalytics();
}]);

How to handle the authentication using 3rd party login in Angularjs

By running the code below, the authentication window pops-up and the user confirms the login. This part works. Once the authorize button is clicked, this redirects to the previous tab in the same window (in the pop-up not in the parent window). How I can close this pop-up window after authorization is confirmed by the user and how can I get back the authorization code from the url? For instance in the code below, the first "console.log(event.url);" is not executed.
var redirectUri = "http://localhost:8100/callback";
var ref = window.open('https://www.example.com/oauth/authorize?client_id=' + clientID + '&redirect_uri=' + redirectUri + '&scope=write&response_type=code&approval_prompt=force', '_blank', 'location=no,clearsessioncache=yes,clearcache=yes');
ref.addEventListener('loadstart', function (event) { // THIS IS NOT TRIGGERED FOR SOME REASON
console.log(event.url);
if ((event.url).indexOf(redirectUri) === 0) {
requestToken = (event.url).split("code=")[1];
$http.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
$http({
method: "post",
url: "https://www.example.com/oauth/token",
data: "client_id=" + clientId + "&client_secret=" + clientSecret + "&code=" + requestToken
})
.success(function (data) {
$scope.data = data;
console.log(event.url);
})
.error(function (data, status) {
deferred.reject("Problem authenticating");
});
}
});
Below are the tabs used in the application. How can I return to my tab.example after callback?
// setup an abstract state for the tabs directive
.state('tab', {
url: '/tab',
abstract: true,
templateUrl: 'templates/tabs.html'
})
.state('tab.example', {
url: '/example',
views: {
'tab-example': {
templateUrl: 'templates/tab-example.html',
controller: 'ExampleAPICtrl'
}
}
})
.state('callback', {
});
// if none of the above states are matched, use this as the fallback
$urlRouterProvider.otherwise('/');
You need a service to wrap the 3rd party Provider so it can listen for the event Call back.
a great library implementation I've used:
mrzmyr Angular-Google-Plus
The heart of the librarie's approach is in the following code snippet:
NgGooglePlus.prototype.login = function () {
deferred = $q.defer();
gapi.auth.authorize({
client_id: options.clientId,
scope: options.scopes,
immediate: false,
approval_prompt: "force",
max_auth_age: 0
}, this.handleAuthResult);
return deferred.promise;
};
When you want to do login with 3rd party, i advice you to create a login service which will perform the login by sending the good information to the login system (other API, web application with url...).
Like that, your service can emit event that can be used in your application to perform more action
$scope.$broadcast('loadstart', {
someProp: 'send parameter to your event in the data object'
});
$scope.$on('loadstart', function (event, data) {
//your function the tretment of the reply
});
If you want to go forward with the event you can follow this link : https://toddmotto.com/all-about-angulars-emit-broadcast-on-publish-subscribing/
to return in your tab.example you can try the $state in the .success part of your http request. This service allow you to choose on which state you want to be :
$state.go('tab.example');

Angular 1.5.0 - Why is factory called only once?

I have an html template that displays data from an Angular factory. The problem is that the factory's get method performs call to the backend only when the page is loaded for the first time. When repeatedly opening the page I only see results from the first call to the backend. It looks like the factory somehow caches the results.
With using Chrome debug tools I see that Angular makes GET requests with the same resource id on each page load.
This is the factory definition:
.factory('Company', ['$resource', '$routeParams', function ($resource, $routeParams) {
return $resource('/companies/getCompany/:companyId', {}, {
get: {
method: 'GET',
url: '/companies/getCompany/:companyId',
params: {
'companyId': $routeParams.companyId
}
},
save: {
method: 'POST',
url: '/companies/updateCompany'
},
insert: {
method: 'POST',
url: '/companies/createNewCompany'
}
});
}])
This is the controller code
.controller('MyController', ['$scope', '$location', 'Company',
function ($scope, $location, Company) {
Company.get(function (data) {
$scope.company = data;
});
}]);
I'm using ng-click to open the page
<tr ng-repeat="company in companies"
ng-click="redirectToCompanyForm(company.id)">
$scope.redirectToCompanyForm = function (companyId) {
$location.url('/updateCompany/' + companyId);
}
I set a breakpoint on the factory - app pauses only the first time when I access the page.
Why is my factory called only once and how can I solve this?
From the Angular docs:
Note: All services in Angular are singletons. That means that the injector uses each recipe at most once to create the object. The injector then caches the reference for all future needs.
So you are right, all services are only created once and then cached by Angular.
Edit: better answer for your situation below:
$resource caching
You can disable caching the resource by adding the options to the $resource call:
return $resource('/companies/getCompany/:companyId', {}, {
get: {
method: 'GET',
params: {
'companyId': $routeParams.companyId
},
cache: false // THIS LINE
}
}
Edit 2: according to the docs, the first parameter of $resource is not optional and must be an url.
Maybe you can use low level $http methods in your controller.
$resource is a fantastic utility but in your case you don't want persist the data.
Try the $http.get method..
Or try the query() method.
$scope.myData = DataFactory.query();
Finally fixed the issue. Factory use was incorrect. This is the factory module
.factory('Company', ['$resource', function ($resource) {
return $resource('/companies/getCompany/:id', null, {
save: {
method: 'POST',
url: '/companies/updateCompany'
},
insert: {
method: 'POST',
url: '/companies/createNewCompany'
}
});
}])
And this is how the factory should be called
Company.get({id: $routeParams.companyId}, function (data) {
$scope.company = data;
});
Now the correct data is shown everytime the page is loaded.

Passing information previously retrieved to templateProvider in AngularJS

I'm using AngularJS 1.3 and UI-Router. I have an state in which i have a resolve and a templateProvider.
What i'm trying to accomplish is that the information retrieved from database in the resolve can be used by the templateProvider. Right now, I have to get the information twice, once from resolve and another from templateProvider, and that's annoying.
The code:
.state('articleurl', {
url: '/:articleUrl',
resolve: {
article: function ($http, $stateParams, $location) {
return $http({
method: 'GET',
url: '/articles/' + $stateParams.articleUrl
})
.then(function (article) {
return article;
}, function (error) {
$location.path('/404');
});
},
loggedin: checkLoggedin
},
templateProvider: ['$templateFactory', '$stateParams', '$http', function ($templateFactory, $stateParams, $http) {
return $http({
method: 'GET',
url: '/articles/' + $stateParams.articleUrl
}).then(function(article) {
if ( article.data.template )
return $templateFactory.fromUrl('articles/views/templates/' + article.data.template + '.html');
else
return $templateFactory.fromUrl('articles/views/templates/news.html');
});
}],
controller: 'ArticlesViewController'
})
As you can see, according to article's kind i load a different template in the templateProvider. Besides, i use the article's information in the controller which has been previously got in the state's resolve.
Is there any way to use in the templateProvider the information previously fetched in the resolve avoiding this way another call to database?
Right now, it is doing 2 calls to database per connection...
Thanks!
app.factory('article', function ($cacheFactory){
var articleCache = $cacheFactory('article');
return function (url) {
return articleCache.get(url) || articleCache.put(url, $http({
method: 'GET',
url: '/articles/' + url
})
);
};
});
Use it as article($stateParams.articleUrl).then(...) in both places, that will keep the things DRY. You may get better control over the cache (e.g. expiration) by replacing $cacheFactory with angular-cache.
$http own caching may be successfully used as well instead of explicit caching:
If there are multiple GET requests for the same URL that should be
cached using the same cache, but the cache is not populated yet, only
one request to the server will be made and the remaining requests will
be fulfilled using the response from the first request.
I think you can inject directly the resolved variables, so you could inject article in templateProvider:
.state('articleurl', {
url: '/:articleUrl',
resolve: {
article: function ($http, $stateParams, $location) {
return $http({
method: 'GET',
url: '/articles/' + $stateParams.articleUrl
})
.then(function (article) {
return article;
}, function (error) {
$location.path('/404');
});
},
loggedin: checkLoggedin
},
templateProvider: ['$templateFactory', '$stateParams', '$http', 'article', function ($templateFactory, $stateParams, $http, article) {
// Now here you can use article without the need to re-call it
}],
controller: 'ArticlesViewController'
})

AngularJS 1.2.0 $resource PUT/POST/DELETE do not send whole object

I'm using angularjs 1.2.0 with $resource. I would like to have some PUT/POST instance actions that doesn't send the whole object to the server but only some fields and in some cases totally no data.
Is it possible? I searched everywhere but couldn't find anything
UPDATE:
It also happens with DELETE requests:
Given this code:
group.$deleteChatMessage({messageId: message.id}, function(){
var i = _.indexOf(group.chat, message);
if(i !== -1) group.chat.splice(i, 1);
});
The request is this:
See how the whole model is sent (under "Request Payload").
This is the resource:
var Group = $resource(API_URL + '/api/v1/groups/:gid',
{gid:'#_id', messageId: '#_messageId'},
{
deleteChatMessage: {method: "DELETE", url: API_URL + '/api/v1/groups/:gid/chat/:messageId'},
});
This works for me:
$resource(SERVER_URL + 'profile.json',
{},
{
changePassword :
{
method : 'POST',
url : SERVER_URL + 'profile/changePassword.json',
// Don't sent request body
transformRequest : function(data, headersGetter)
{
return '';
}
}
});
You could customise exaclty what is sent to the server by implementing your own code in the transformRequest function. In my example I was adding a new function to the REST client, but you can also overwrite existing functions. Note that 'transformRequest' is only available in version 1.1+
You can use $http for that specifically. However, I have one case I use for a project that might help. Also my example is returning an array from the server but you can change that.
In my service:
app.factory('mySearch', ['$resource', function($resource) {
return $resource('/api/items/:action', {}, {
search: { method: 'POST', isArray: true,
params: { action: 'search' }
}
});
}
]);
In my Controller:
I can build up custom params to post to server or if its only two fields I need from a table row the user selects.
var one = "field_one";
var two = "field_two";
$scope.search({one: one, two: two});
Then I can post those through an event and pass the custom params
$scope.search = function(customParams) {
mySearch.search({query: customParams}, function(data) {
$scope.items = data;
}, function(response) {
console.log("Error: " + response.status);
})
};
Hopefully this was some help. Let me know if this is close to what your looking for and I can help more.
POST
DELETE

Categories

Resources