Cache Blob images in AngularJS - angularjs

I've a WebApp in AngularJS and load data-url images from a REST service.
I want to cache these data urls.
Is that possible?
the gerenal get function (which I use everywhere)
return $http.get(url)
.then(function (response) {
var call = new HttpCall("get", url, null, response.data);
if (logging.http) {
$log.debug(call);
}
return call;
}, function (error) {
var call = new HttpCall("get", url, null, error);
if (logging.http || logging.error) {
$log.error(call);
}
return call;
});
HttpCall is just a container, for better logging output. The response is stored in call.response and the error in call.error.

Related

Custom cache for HTTP requests

I want to cache the response [i.e. parsed JSON response] of HTTP requests rather than the response itself. My data is big and gzipped so there is actually a fair performance hit decompressing this so would like to store the raw data itself.
Currently I am using a HTTP Interceptor for caching and TimeToLive mechanics described here alongside AngularJS' built in $cacheFactory.
So how can I, using an intercepter, stop the HTTP request and return my own response. Note I still plan on using $cacheFactory, I'd just manage my own data.
.factory('cacheInterceptor', ['$cacheFactory', function($cacheFactory) {
return {
request: function(config) {
if (config.cache) {
// if we have stored this request, return it, else let the request happen naturally and cache after
// Things I don't know:
// How to return existing cache data and prevent the reqeust from happening
// Cache the data I get back from a HTTP request
}
return config;
}
};
}])
I would preffer to inject this into your service and make your factory only handle the data recived/cached. This time I only created a service for you which holds the logic of HTTP / Cache switch. I think you will be able to create a factory to handle your data/states on your own.
.service('getService', ['$cacheFactory', '$http', '$q', function($cacheFactory, $http, $q) {
return {
request: function() {
function getData () {
var deferred = $q.defer();
if (angular.isUndefined($cacheFactory.get('getServiceData'))) {
$http({
'method': 'GET',
'url': 'someUrl'
}).then(function (result) {
$cacheFactory.put('getServiceData', result.data);
deferred.resolve(result.data);
});
} else {
deferred.resolve($cacheFactory.get('getServiceData'));
}
}
return getData();
},
flush: function () {
$cacheFactory.remove('getServiceData');
},
refresh: function () {
this.flush();
return this.refresh();
}
};
}]);
it's enough to add {cache: true} to the request options.
see the here
$http.get('some/url', {cache: true})
.then( ({data}) => data)

Custom error message with content-type response

I'm trying to implement some error handling into my MCV AngularJS application, but came across this one issue that I'm not sure how to solve.
Structure
In my AngularJS service ticketService I have the following method:
this.downloadFile = function (fileId) {
return $http.get(baseUrl + "/Downloadfile/" + fileId, { responseType: "blob" });
}
And in my controller:
$scope.downloadFile = function (fileId) {
ticketService.downloadFile(fileId)
.then(function (response) {
// Handle correct request and response
}, function (err) {
// Handle error
notify({ message: "Something went wrong: " + err.data.Message, position: "center", duration: 10000 });
})
}
Here's what I return from the backend MVC Web API method:
var error = new HttpError("Failed to find file, bla bla bla.");
return Request.CreateResponse(HttpStatusCode.NotFound, error);
Problem
My issue is that since my responseType is set to be blob, my err object is the same response type. I would believe that it should be possible for my backend service to override this response type, and respond with an object that contains some Message.
From this response, I would've thought that I could get err.data.Message, but perhaps I misunderstood this scenario?
Thank you in advance.
public HttpResponseMessage Get(int id)
{
try
{
return Request.CreateResponse(HttpStatusCode.OK, new { Status =
"OK", Message = this._myContext.GetCustomer(id) });
}
catch(Exception e)
{
return Request.CreateResponse(HttpStatusCode.Conflict, new {
Status = "NO", Message = e.ToString() });
}
}
You can return any message like "Failed to find file, bla bla bla." in Message. Then you just need to check in ajax success method like data.Message,
The $http service uses the XHR API which is not capable of changing the responseType on the fly.
You can set the status message and use that:
public ActionResult Foo()
{
Response.StatusCode = 403;
Response.StatusDescription = "Some custom message";
return View(); // or Content(), Json(), etc
}
Then in AngularJS:
$scope.downloadFile = function (fileId) {
return ticketService.downloadFile(fileId)
.then(function (response) {
// Handle correct request and response
return response.data;
}).catch(function (response) {
// Handle error
console.log(response.status);
console.log(response.statusText);
throw response;
});
};
Alternative approaches are:
Use the Fetch API which has a more powerful and flexible feature set.
Use the FileReader API and JSONparse() method to convert the Blob to a JavaScript Object.

Calling $http.delete but nothing happens

I'm actually working on a webclient calling a REST service.
After my last question, the GET request is working now.
Now i want o implement a DEL request using angulars delete method.
In the following example is my service request implemented.
function ItemsService($http) {
this.$http = $http;
}
ItemsService.prototype = {
deleteFoo: function (id) {
this.$http.delete('http://localhost:3001/posts/' + id)
.then(function successCallback(response) {
console.log("DEL send...");
console.log(response.data);
}, function errorCallback(response) {
console.log('Error');
});
}
}
module.exports = {
ItemsService: ItemsService
}
I added a button on the webpage with ng-click="$ctrl.deleteMe()".
The controller looks like the following example:
function Foo(ItemsService) {
this.itemsService = ItemsService;
}
Foo.prototype = {
deleteMe: function () {
console.log('delete now');
this.itemsService.deleteFoo(1).then(
response => {
console.log('gelöscht? ' + response);
}
);
}
};
If i now click on the button, nothing happens. In the network trace log in the dev tools in the browser i can't see a DEL request.
To test this REST service request, i run the JSON Server tool on port 3001.
I testet the availability of the server with SOAPUI, it works, i see all the requests in the console.
But no request from my test webpage.
Can anyone help me?
You need to return
return this.$http.delete('http://localhost:3001/posts/' + id)
.then(function successCallback(response) {
console.log("DEL send...");
console.log(response.data);
}, function errorCallback(response) {
console.log('Error');
});

Promise Chains in angularjs

I am uploading attachments using rest api in SharePoint 2013,for this I need to call upload attachment method on synchronous.
Because If I call upload attachment method asynchronous I am getting 409 conflict error.
How to chain promise objects in for loop.i.e I want to call second attachment method in first attachment success and so on..
Please help me in best approach of chaining of promises in for loop.
Common method for saving attachments:
var saveFileAngularJS = function (file, url) {
var deferred = $q.defer();
getFileBuffer(file).then(function (fileArrBuffer) {
$http({
method: 'POST',
url: baseUrl + url,
headers: {
'Accept': 'application/json;odata=verbose',
'Content-Type': undefined,
'X-RequestDigest': jQuery("#__REQUESTDIGEST").val()
},
data: new Uint8Array(fileArrBuffer),
transformRequest: []
}).then(function successCallback(data) {
deferred.resolve(data);
alert('Successfully saved.', data);
}, function errorCallback(error) {
deferred.reject(error);
alert('Failed to save!!!.', error);
});
});
return deferred.promise;
};
Method calling :
for (var i = 0; i < $scope.files.length; i++) {
var file = $scope.files[i]._file;
var response = lssDealService.insertAttachment(transactionId, file);
}
var insertAttachment = function (dealId, file) {
var attachmentUrl = listEndPoint + "/GetByTitle('TransactionList')/GetItemById(" + dealId + ")/AttachmentFiles/add(FileName='" + file.name + "')";
return baseService.saveFile(file, attachmentUrl);
};
Insert attachment will call SaveFile method.
I want to run this for loop sequentially, once the loop has been completed I need to process all promises and display success message to user.
Please help me to writing the chaining promises in effective way.
Lets say you have the attachements as an array,
function uploadMyAttachements() {
return myAttachements.reduce(function(promise, attachment) {
return promise.then(function () {
return upload(attachment);
})
.then(function(result) {
console.log('RESULT FOR LAST UPLOAD', result);
});
}, Promise.resolve());
}
function upload(attachment) {
//upload the attachment to sharepoint
//and return a promise here
}
uploadMyAttachements().catch(function(err) {
//if anything in the promise chain fails
//it stops then and there and CATCHED here
});
Now whats happening here, using the Array.reduce, we create a chain of promises like shown below
upload(0).then(handleResult_0).upload(1).then(handleResult_1)....
and it execute one by one as you expected
Throwing my 2 pennies:
$scope.attachments = []; //modified via binding.
function uploadAttachments(){
//Reduce the files array into a promise array with the uploadOne method
//then return the promise when every promise has been resolved or one has rejected.
return $q.all($scope.attachments.reduce(uploadOne, []));
}
function uploadOne(file){
//Upload one, return promise. Use $http or $resource.
}
//Note - a more advanced way of doing this would be to send the files as batch (one
//$http post) as FormData. There are some good wrappers for angular.
$scope.upload = function(){
uploadAttachments().then(function(results){
//Array of results
}).catch(function(e){
//Error handler
});
}

Chaining Angular Promises

I am having some trouble chaining promises in Angular. What I want to do is fetch my project object from the API, then check if the project owner has any containers, if they do, trigger the another GET to retrieve the container. In the end the container assigned to scope should either be null or the object retrieved from the API.
Right now, this example below resolves immediately to the second then function, and I get the error, TypeError: Cannot read property 'owner' of undefined. What am I doing wrong?
$http.get('/api/projects/' + id).then(function (data) {
$scope.project = data.project;
return data.project;
}).then(function (project) {
var containers = project.owner.containers;
if (containers.length) {
return $http.get('/api/containers/' + containers[0]);
} else {
return null
}
}).then(function (container) {
$scope.container = container;
});
Ah, turns out the data from passed into then is inside a field, so I needed to do
$scope.project = data.data.project;
return data.data.project;
Your example code works, but what if the $http call fails because of a 404? Or you want to later want to add some extra business logic?
In general you want to handle 'negative' cases using a rejecting promise, to have more control over the chaining flow.
$http.get('/api/projects/' + id).then(function (data) {
$scope.project = data.data.project;
return data.data.project;
}).then(function (project) {
var containers = project.owner.containers;
if (containers.length) {
return $q.reject('containers empty');
}
return $http.get('/api/containers/' + containers[0]);
}).then(function (container) {
$scope.container = container;
}).except(function (response) {
console.log(response); // 'containers empty' or $http response object
$scope.container = null;
});

Resources