HttpRequest Global Listeners #9381
Labels
area-core-library
SDK core library issues (core, async, ...); use area-vm or area-web for platform specific libraries.
closed-obsolete
Closed as the reported issue is no longer relevant
library-html
P2
A bug or feature request we're likely to work on
type-enhancement
A request for a change that isn't a bug
Thats not exactly a problem, but a core feature or improvement. As a web developer I want be able to listen to all request across libraries, so I can attach behavior like security permission across client and server, or simply give a nice user feedback, or trying to connect again to the server. The point here is that I can't do this using Futures, because a Future is bonded to the caller and I can't listen everything. One workaround is to build a singleton class that wraps the HttpRequest class and construct the HttpRequest, attach you own listen before returning and them forwards the events to the outside world in a static or global way. But that is not global because other libraries will not be using my class they will use the HttpRequest and the custom class will not be able to catch this events.
There is a use case:
After all request the server send a custom HttpCode that mean the user does not have rights to do that call, and than a global listen can inspect for that request and make I nice message to the user telling this and giving he a option to login as a different user, or even disabling the button that has clicked. I'm not speaking about a web page I'm speaking about a only ajax application where you can't do this in the server because all UI is running on the client and you don't have page loads (the page load only once)
Another use case is, well we need give feedback to the users, and is simple but not likely write the same code across the code using futures, well you of course put that in a separate reusable class, but you still have to call this class everywhere, with global listen you attach that once and give the proper feedback. This two concepts listeners and Futures work very well together, you can still make your request and use the response latter and the specific code bonded with the caller can still runs as well the listener at the same time.
Maybe I just don't see how to do this in the HttpRequest class or any else class.
But here is a implementation for a custom class that wraps the HttpRequest class and is working, but as I said I can do this only for my code the other libraries do not know of the existence of this class, I will call it Ajax class:
/**
/
class Ajax implements HttpRequest {
// The stream methods to dispatch
static StreamController _onAbortStreamController = new StreamController.broadcast();
static StreamController _onErrorStreamController = new StreamController.broadcast();
static StreamController _onLoadStreamController = new StreamController.broadcast();
static StreamController _onLoadEndStreamController = new StreamController.broadcast();
static StreamController _onLoadStartStreamController = new StreamController.broadcast();
static StreamController _onProgressStreamController = new StreamController.broadcast();
/*
* Return a request that always have one listen to forward globally
* Dispatches the event for the world. All the events that this class support have to be added here
* This method always return a new HttpRequest
*/
factory Ajax(){
HttpRequest request = new HttpRequest();
request.onAbort.listen( (event) => _onAbortStreamController.add(event));
request.onError.listen( (event) => _onErrorStreamController.add(event));
request.onLoad.listen( (event) => _onLoadStreamController.add(event));
request.onLoadEnd.listen( (event) => _onLoadEndStreamController.add(event));
request.onLoadStart.listen( (event) => _onLoadStartStreamController.add(event));
request.onProgress.listen( (event) => _onProgressStreamController.add(event));
return request;
}
/**
* Ajax return HttpRequest so, the constructor is private
*/
Ajax._private(){
}
/// A global stream for onAbort
static Stream<HttpRequestProgressEvent> get onGlobalAbort => _onAbortStreamController.stream;
/// A global stream for onError
static Stream<HttpRequestProgressEvent> get onGlobalError => _onErrorStreamController.stream;
/// A global stream for handle onLoad
static Stream<HttpRequestProgressEvent> get onGlobalLoad => _onLoadStreamController.stream;
/// A global stream for handle onLoadEnd
static Stream<HttpRequestProgressEvent> get onGlobalLoadEnd => _onLoadEndStreamController.stream;
/// A global stream for handle onLoadStart
static Stream<HttpRequestProgressEvent> get onGlobalLoadStart => _onLoadStartStreamController.stream;
/// A global stream for handle onProgress
static Stream<HttpRequestProgressEvent> get onGlobalProgress => _onProgressStreamController.stream;
}
A exemple of how to use this class. Lets say we have a ExceptionView class that will inspect for errors or messages from the server and make a nice popup to the user (using the Notify class):
/**
* Classe para escuta de eventos globais e apresentacao de notificacoes genericas para o usuário.
* Essa classe usa [Notify] e [Ajax]
* A class for listen to global request Events and do nice user feedback. This class uses [Notify] and [Ajax]
* Date: 19/03/13
* Time: 18:22
*/
class ExceptionView {
static ExceptionView _instance;
factory ExceptionView(){
if(_instance == null){
_instance = new ExceptionView._private();
}
return _instance;
}
ExceptionView._private() {
Ajax.onGlobalError.listen(_onError);
Ajax.onGlobalLoad.listen(_onSuccess);
// HttpRequest.onErrorEvent.listen(_onError); // I can't do that
// HttpRequest.onLoadEvent.listen(_onSuccess); // I can't do that
}
void _onError(HttpRequestProgressEvent event){
var target = event.target;
if(target.response == null){
new Notify().msg(title: 'Erro ao processar requisição', message: '''Houve um erro desconhecido ao
processar a requisição. Código do Erro: ${t.status}''', useNotificationsApi: false);
}else {
new Notify().msg(title: 'Erro ao processar requisição', message: target.responseText, useNotificationsApi: false);
}
}
/**
* Display messages on the screen, if the message is not a Json message then display a error to the user.
* If the response is a json string and have the property "message" display a message to the user. If not do nothing,
* If the message have the property success with value false and the message property, display a error message
* to the user with the message property
*/
void _onSuccess(HttpRequestProgressEvent event){
Map result;
try {
result = JSON.parse(event.target.responseText);
} catch(e){
/// If the response is not Json treat as Error, I don't use the Notifications API because the response can be a very
/// long server response like a Http Page.
new Notify().msg(title: 'Ocorreu um erro no servidor',message: event.target.responseText, useNotificationsApi: false,
stay: true );
}
// Only show a message if there is a message property on the response and the message isn't empty
if(result != null && result['message'] != null){
if(result['message'].trim() == ""){ // If message is a empty string do nothing
return;
}
// Verifica se existe uma propriedade success na resposta e formata-a o titulo de acordo
String title = result['success'] == true || result['success'] == null ? 'OK' : 'Error';
bool stay = result['success'] == true || result['success'] == null ? false : true;
new Notify().msg(title: title, message: result['message'], stay: stay);
}
}
}
I use Dart 0.4.2.8_r20259
Here is a post about this subject on google groups: https://groups.google.com/a/dartlang.org/d/topic/misc/-oQ6eYwuKdg/discussion
Sorry any wrong written but english is not my native language.
Thank you for your time.
The text was updated successfully, but these errors were encountered: