Mobile first - Encrypted Cache Success and Failure Handler - javascript

In MobileFirst V6.3 once we call a JSON Store API, Success and failure can be captured using .then() & .fail(). To chain the API calls we can use multiple .then(). Let's say,
WL.JSONStore.startTransaction()
.then(function () {
var data = [{name: 'carlos'}];
return WL.JSONStore.get(collectionName).add(data);
})
.then(function () {
var docs = [{_id: 1, json: {name: 'carlos'}}];
return WL.JSONStore.get(collectionName).remove(docs);
})
.then(function () {
return WL.JSONStore.commitTransaction();
})
.fail(function (errorObject) {
WL.JSONStore.rollbackTransaction()
.then(function () {
// Handle rollback success.
})
.fail(function () {
// Handle rollback failure.
})
});
Since Encrypted Cache API has its own API's callback methods, like below.
WL.EncryptedCache.open(credentials, create_if_none, onCompleteHandler, onErrorHandler);
How to handle Encrypted Cache API chain call's similar to JSON Store[Avoiding callback methods for each API Call's]?
If its not available in out-of-box, is any work around available to achieve the same.
A snippet will be helpful.

The recommendation is to use JSONStore.
Chaining callbacks is not supported out of the box.
The way to do it, is for someone to implement wrappers for the methods that are using callbacks. If you insist on doing that, you'll need to implement something that will look like this:
function wrapper() {
var myVar = $.Deferred();
Wl.EncryptedCache.open(credentials, create_if_none, myVar.resolve, myVar.reject);
return myVar;
}
From the user's code:
wrapper.then(
function() {success flow...},
function() {failure flow...}
);

Related

How to use a default reject handler in Promise

I make Ajax requests with a Promise and usually handle errors the same way. So e.g. if a 404 happens, then I would just display a standard error message by default. But in some cases I want to do something else.
Note: I'm using ExtJS 4 to do the actual Ajax request, but this issue is not specific to ExtJS. ExtJS does not use Promises, so I'm basically converting their API to a Promise API.
This is the code:
var defaultErrorHandler = function(response) {
// do some default stuff like displaying an error message
};
var ajaxRequest = function(config) {
return new Promise(function(fulfill, reject) {
var ajaxCfg = Ext.apply({}, {
success: function(response) {
var data = Ext.decode(response.responseText);
if (data.success) {
fulfill(data);
} else {
defaultErrorHandler(response);
reject(response);
}
},
failure: function(response) {
defaultErrorHandler(response);
reject(response);
}
}, config);
Ext.Ajax.request(ajaxCfg);
});
};
// usage without special error handling:
ajaxRequest({url: '/some/request.json'}).then(function(data) {
// do something
});
// usage with special error handling:
ajaxRequest({url: '/some/request.json'}).then(function(data) {
// do something
}, function(response) {
// do some additional error handling
});
Now the problem: The "usage without special error handling" does not work, because if I do not provide a reject function, it will throw an error. To fix this, I am forced to provide an empty function, like so:
// usage without special error handling:
ajaxRequest({url: '/some/request.json'}).then(function(data) {
// do something
}, function() {});
Having to provide an empty function every time (and in my code base this will be hundreds of times) is ugly, so I was hoping there was a more elegant solution.
I also do not want to use catch() since that would catch ALL errors thrown, even if it happens in the fulfill function. But actual errors happening in my code should not be handled, they should appear in the console.
There is no such thing a "default error handler for all promises", unless you are looking to provide an unhandled rejection handler. That would however not be restricted to the promises for your ajax requests.
The simplest and best solution would be to just expose your defaultErrorHandler and have every caller explicitly pass it the then invocation on your promise. If they don't want to use it, they either need to provide their own special error handler or they will get a rejected promise. This solution provides maximum flexibility, such as allowing to handle the rejection further down the chain.
If that is not what you want to do, but instead require immediate handling of the ajax error, your best bet is to override the then method of your returned promises:
function defaultingThen(onfulfill, onreject = defaultErrorHandler) {
return Promise.prototype.then.call(this, onfulfill, onreject);
}
function ajaxRequest(config) {
return Object.assign(new Promise(function(resolve, reject) {
Ext.Ajax.request({
...config,
success: function(response) {
var data = Ext.decode(response.responseText);
if (data.success) {
resolve(data);
} else {
reject(response);
}
},
failure: reject,
});
}), {
then: defaultingThen,
});
}

Using service, ngResource save

I'm new to Angular.
I have this Service(?) for my RESTful services.
.factory('LanguagesService', function ($resource) {
return $resource('http://127.0.0.1:8000/language/:langId', {
langId: '#id'
});
});
Then in my controller I do like this
adminLang.addLanguage = function () {
LanguagesService.save({
code: adminLang.newCode,
name: adminLang.newName
});
}
My question is, how do I know if the save() is successful? So I can do this or that depending on if it fails or succeeds?
Thank you a lot.
When you request add two callback functions as below:
LanguagesService.save({
code: adminLang.newCode,
name: adminLang.newName
}, function(response){
// success
}, function(error){
// error
});
Check this for more information: http://fdietz.github.io/recipes-with-angular-js/consuming-external-services/consuming-restful-apis.html
$resource methods returns a promise object via $promise object, you could keep eye on that promise by placing .then.
Code
LanguagesService.save({
code: adminLang.newCode,
name: adminLang.newName
}).$promise.then(function(data){
console.log(data);
//do other awesome things
}, function(err){
});

Ember Understand execution flow between route/controller

I have a "box" route/controller as below;
export default Ember.Controller.extend({
initialized: false,
type: 'P',
status: 'done',
layouts: null,
toggleFltr: null,
gridVals: Ember.computed.alias('model.gridParas'),
gridParas: Ember.computed('myServerPars', function() {
this.set('gridVals.serverParas', this.get('myServerPars'));
this.filterCols();
if (!this.get('initialized')) {
this.toggleProperty('initialized');
} else {
Ember.run.scheduleOnce('afterRender', this, this.refreshBox);
}
return this.get('gridVals');
}),
filterCols: function()
{
this.set('gridVals.layout', this.get('layouts')[this.get('type')]);
},
myServerPars: function() {
// Code to set serverParas
return serverParas;
}.property('type', 'status', 'toggleFltr'),
refreshBox: function(){
// Code to trigger refresh grid
}
});
My route looks like;
export default Ember.Route.extend({
selectedRows: '',
selectedCount: 0,
rawResponse: {},
model: function() {
var compObj = {};
compObj.gridParas = this.get('gridParas');
return compObj;
},
activate: function() {
var self = this;
self.layouts = {};
var someData = {attr1:"I"};
var promise = this.doPost(someData, '/myService1', false); // Sync request (Is there some way I can make this work using "async")
promise.then(function(response) {
// Code to use response & set self.layouts
self.controllerFor(self.routeName).set('layouts', self.layouts);
});
},
gridParas: function() {
var self = this;
var returnObj = {};
returnObj.url = '/myService2';
returnObj.beforeLoadComplete = function(records) {
// Code to use response & set records
return records;
};
return returnObj;
}.property(),
actions: {
}
});
My template looks like
{{my-grid params=this.gridParas elementId='myGrid'}}
My doPost method looks like below;
doPost: function(postData, requestUrl, isAsync){
requestUrl = this.getURL(requestUrl);
isAsync = (isAsync == undefined) ? true : isAsync;
var promise = new Ember.RSVP.Promise(function(resolve, reject) {
return $.ajax({
// settings
}).success(resolve).error(reject);
});
return promise;
}
Given the above setup, I wanted to understand the flow/sequence of execution (i.e. for the different hooks).
I was trying to debug and it kept hopping from one class to another.
Also, 2 specific questions;
I was expecting the "activate" hook to be fired initially, but found out that is not the case. It first executes the "gridParas" hook
i.e. before the "activate" hook. Is it because of "gridParas"
specified in the template ?
When I do this.doPost() for /myService1, it has to be a "sync" request, else the flow of execution changes and I get an error.
Actually I want the code inside filterCols() controller i.e.
this.set('gridVals.layout', this.get('layouts')[this.get('type')]) to
be executed only after the response has been received from
/myService1. However, as of now, I have to use a "sync" request to do
that, otherwise with "async", the execution moves to filterCols() and
since I do not have the response yet, it throws an error.
Just to add, I am using Ember v 2.0
activate() on the route is triggered after the beforeModel, model and afterModel hooks... because those 3 hooks are considered the "validation phase" (which determines if the route will resolve at all). To be clear, this route hook has nothing to do with using gridParas in your template... it has everything to do with callling get('gridParas') within your model hook.
It is not clear to me where doPost() is connected to the rest of your code... however because it is returning a promise object you can tack on a then() which will allow you to essentially wait for the promise response and then use it in the rest of your code.
Simple Example:
this.doPost().then((theResponse) => {
this.doSomethingWith(theResponse);
});
If you can simplify your question to be more clear and concise, i may be able to provide more info
Generally at this level you should explain what you want to archive, and not just ask how it works, because I think you fight a lot against the framework!
But I take this out of your comment.
First, you don't need your doPost method! jQuerys $.ajax returns a thenable, that can be resolved to a Promise with Ember.RSVP.resolve!
Next: If you want to fetch data before actually rendering anything you should do this in the model hook!
I'm not sure if you want to fetch /service1, and then with the response you build a request to /service2, or if you can fetch both services independently and then show your data (your grid?) with the data of both services. So here are both ways:
If you can fetch both services independently do this in your routes model hook:
return Ember.RSVP.hash({
service1: Ember.RSVP.resolve($.ajax(/*your request to /service1 with all data and params, may use query-params!*/).then(data => {
return data; // extract the data you need, may transform the response, etc.
},
service2: Ember.RSVP.resolve($.ajax(/*your request to /service2 with all data and params, may use query-params!*/).then(data => {
return data; // extract the data you need, may transform the response, etc.
},
});
If you need the response of /service1 to fetch /service2 just do this in your model hook:
return Ember.RSVP.resolve($.ajax(/*/service1*/)).then(service1 => {
return Ember.RSVP.resolve($.ajax(/*/service2*/)).then(service2 => {
return {
service1,
service2
}; // this object will then be available as `model` on your controller
});
});
If this does not help you (and I really think this should fix your problems) please describe your Problem.

Why doesn't the Reflux.js listenAndPromise helper work?

I'm using qwest to query my endpoint as shown below, the onGetResourceCompleted handler fires as expected but data is undefined. Why?
var Actions = Reflux.createActions({
'getResource': { asyncResult: true }
});
Actions.getResource.listenAndPromise(function (id) {
return qwest.get('http://localhost:8000/my-data/'+id, null, { withCredentials: true });
});
var MyStore = Reflux.createStore({
listenables: Actions,
init: function () {
Actions.getResource('');
},
onGetResourceCompleted: function (data) {
console.log('OK', data); // Get's called but data is undefined. Why?
}
});
I can see the data loads correctly by looking at dev tools as well as calling qwest in isolation by simply doing:
qwest.get('http://localhost:8000/my-data/'+id, null, { withCredentials: true }).then(function(data) {
console.log('OK', data);
});
Also doing the following works:
ServiceActions.getResource.listen(function (id) {
ServiceActions.getResource.promise(
qwest.get('http://localhost:8000/my-data/'+id, null, { withCredentials: true })
);
});
I've put some comments on the cause of this "confirmed bug" in the original issue you opened at github.com/spoike/refluxjs.
So, though you are using the reflux features the way they are intended, and they're definitely creating a race condition without even returning the race results, I think you're in luck. It turns out the two particular features you're using in this combination with this type of request is a bit redundant when you already have a promise available. I'd recommend you just drop the onGetRequestCompleted handler entirely, and handle completion using the standard promise ways of handling resolved promises, which honestly will give you more flexibility anyways.
For example:
var MyStore = Reflux.createStore({
listenables: Actions,
init: function () {
Actions.getResource('')
.then() <-- this eliminates the need for onGetResourceCompleted
.catch() <-- or this instead/in addition
.finally() <-- or this instead/in additon
},
// no more onGetResourceCompleted
});

angular.js ui + bootstrap typeahead + asynchronous call

I'm using typeahead with the angular.js directive but my function to populate the autocomplete makes an asynchronous call and I can't return it to populate the autocomplete. Is there anyway to make it work with this asynchronous call?
Can I assume that you are using the typeahead of Bootstrap 2.x ?
If so, in the documentation, the description of the source field of typeahead()'s options is this:
The data source to query against. May be an array of strings or a
function. The function is passed two arguments, the query value in the
input field and the process callback. The function may be used
synchronously by returning the data source directly or asynchronously
via the process callback's single argument.
You can definitely pass in an async function as the source attr. The source function could be something like:
function someFunction(query, process) {
someAsyncCall(...query or so... , function(res) { // success callback
process(res);
}, function(err) { // error callback
process(null);
});
}
Update:
If you are using Angular Bootstrap's typeahead, it should be even easier. According to Angular Bootstrap's docs(http://angular-ui.github.io/bootstrap/), you can just return a promise for the typeahead function. Some example from the docs:
$scope.getLocation = function(val) {
return $http.get('http://maps.googleapis.com/maps/api/geocode/json', {
params: {
address: val,
sensor: false
}
}).then(function(res){
var addresses = [];
angular.forEach(res.data.results, function(item){
addresses.push(item.formatted_address);
});
return addresses;
});
};
A simpler one could be:
$scope.getSomething= function(query) {
var promise = $http.get('...some url...', {
params: {
queryName: query
}
});
return promise;
};
Or you can build your own promise:
$scope.getSomething= function(query) {
var deferred = $q.defer();
someAsyncCall(...query or so... , function(res) { // success callback
deferred.resolve(res);
}, function(err) { // error callback
deferred.reject(err);
});
return deferred.promise;
};
Actually, many services like $http are just returning promises when you call them.
More about promise in AngularJS: https://docs.angularjs.org/api/ng/service/$q

Categories

Resources