J'essaie de comprendre comment utiliser la propriété timeout d'une ressource $ pour annuler dynamiquement les demandes en attente. Idéalement, j'aimerais simplement pouvoir annuler les demandes avec certains attributs (en fonction des paramètres envoyés), mais il semble que cela ne soit pas possible. En attendant, j'essaie simplement d'annuler toutes les demandes en attente, puis de réinitialiser la promesse d'expiration pour autoriser les nouvelles demandes.
Le problème semble être que la configuration de $ resource n'autorise qu'une seule promesse statique pour la valeur du délai d'expiration. Il est logique que je puisse faire cela si je faisais des appels $ http individuels, car je pouvais simplement passer de nouvelles promesses pour le délai d'attente, mais comment cela peut-il fonctionner pour une ressource $? J'ai mis en place un exemple de plunker ici: http://plnkr.co/edit/PP2tqDYXh1NAOU3yqCwP?p=preview
Voici mon code de contrôleur:
app.controller('MainCtrl', function($scope, $timeout, $q, $resource) {
$scope.canceller = $q.defer();
$scope.pending = 0;
$scope.actions = [];
var API = $resource(
'index.html', {}, {
get: {
method: 'GET',
timeout: $scope.canceller.promise
}
}
)
$scope.fetchData = function() {
if ($scope.pending) {
$scope.abortPending();
}
$scope.pending = 1;
$scope.actions.Push('request');
API.get({}, function() {
$scope.actions.Push('completed');
$scope.pending = 0;
}, function() {
$scope.actions.Push('aborted');
});
}
$scope.abortPending = function() {
$scope.canceller.resolve();
$scope.canceller = $q.defer();
}
});
À l'heure actuelle, l'annuleur fonctionne lorsqu'une demande est en attente, mais je ne semble pas être en mesure de la réinitialiser - une fois qu'une demande est abandonnée, toutes les demandes futures seront également abandonnées.
Je suis sûr que je manque quelque chose, car être en mesure d'annuler les demandes en attente semble être une caractéristique assez cruciale de la plupart des applications Web (au moins que j'ai construites).
Merci
Réponse de Gecko IT fonctionne pour moi, mais j'ai dû faire quelques modifications pour:
Il s'agit d'une implémentation complète de l'usine de services (il vous suffit de mettre le nom de module approprié):
'use strict';
/**
* ResourceFactory creates cancelable resources.
* Work based on: https://stackoverflow.com/a/25448672/1677187
* which is based on: https://developer.rackspace.com/blog/cancelling-ajax-requests-in-angularjs-applications/
*/
/* global array */
angular.module('module_name').factory('ResourceFactory', ['$q', '$resource',
function($q, $resource) {
function abortablePromiseWrap(promise, deferred, outstanding) {
promise.then(function() {
deferred.resolve.apply(deferred, arguments);
});
promise.catch(function() {
deferred.reject.apply(deferred, arguments);
});
/**
* Remove from the outstanding array
* on abort when deferred is rejected
* and/or promise is resolved/rejected.
*/
deferred.promise.finally(function() {
array.remove(outstanding, deferred);
});
outstanding.Push(deferred);
}
function createResource(url, options, actions) {
var resource;
var outstanding = [];
actions = actions || {};
Object.keys(actions).forEach(function(action) {
var canceller = $q.defer();
actions[action].timeout = canceller.promise;
actions[action].Canceller = canceller;
});
resource = $resource(url, options, actions);
Object.keys(actions).forEach(function(action) {
var method = resource[action];
resource[action] = function() {
var deferred = $q.defer(),
promise = method.apply(null, arguments).$promise;
abortablePromiseWrap(promise, deferred, outstanding);
return {
$promise: deferred.promise,
abort: function() {
deferred.reject('Aborted');
},
cancel: function() {
actions[action].Canceller.resolve('Call cancelled');
// Recreate canceler so that request can be executed again
var canceller = $q.defer();
actions[action].timeout = canceller.promise;
actions[action].Canceller = canceller;
}
};
};
});
/**
* Abort all the outstanding requests on
* this $resource. Calls promise.reject() on outstanding [].
*/
resource.abortAll = function() {
for (var i = 0; i < outstanding.length; i++) {
outstanding[i].reject('Aborted all');
}
outstanding = [];
};
return resource;
}
return {
createResource: function (url, options, actions) {
return createResource(url, options, actions);
}
};
}
]);
L'utilisation est la même que dans l'exemple Gecko IT. Usine de service:
'use strict';
angular.module('module_name').factory('YourResourceServiceName', ['ResourceFactory', function(ResourceFactory) {
return ResourceFactory.createResource('some/api/path/:id', { id: '@id' }, {
create: {
method: 'POST'
},
update: {
method: 'PUT'
}
});
}]);
Utilisation dans le contrôleur (rétrocompatible):
var result = YourResourceServiceName.create(data);
result.$promise.then(function success(data, responseHeaders) {
// Successfully obtained data
}, function error(httpResponse) {
if (httpResponse.status === 0 && httpResponse.data === null) {
// Request has been canceled
} else {
// Server error
}
});
result.cancel(); // Cancels XHR request
Approche alternative:
var result = YourResourceServiceName.create(data);
result.$promise.then(function success(data, responseHeaders) {
// Successfully obtained data
}).catch(function (httpResponse) {
if (httpResponse.status === 0 && httpResponse.data === null) {
// Request has been canceled
} else {
// Server error
}
});
result.cancel(); // Cancels XHR request
Améliorations supplémentaires:
httpResponse.isCanceled
lorsque la demande est annulée, et similaire pour l'abandon.(pour Angular 1.2.28+) Bonjour à tous, je voulais simplement que cela soit facile à comprendre, comment j'ai géré ce problème comme suit:
Ici, je déclare le paramètre de délai d'expiration
$scope.stopRequestGetAllQuestions=$q.defer();
puis je l'utilise comme suit
return $resource(urlToGet, {}, {get:{ timeout: stopRequestGetAllQuestions.promise }});
et si je veux arrêter les appels de ressources $ précédents, je résous simplement cet objet stopRequestGetAllQuestions qui est tout.
stopRequestGetAllQuestions.resolve();
mais si je veux arrêter les précédents et démarrer un nouvel appel à $ resource alors je le fais après stopRequestGetAllQuestions.resolve();
:
stopRequestGetAllQuestions = $q.defer();
Il existe actuellement de nombreux exemples. Les deux suivants, j'ai trouvé assez informatif:
Celui-ci montre un exemple comment traiter à la fois les demandes $ resource et $ http: https://developer.rackspace.com/blog/cancelling-ajax-requests-in-angularjs-applications/
et
Celui-ci est plus simple et ne concerne que $ http: http://odetocode.com/blogs/scott/archive/2014/04/24/canceling-http-requests-in-angularjs.aspx
Salut, j'ai créé un gestionnaire personnalisé basé sur https: //developer.rackspace.com/blog/...
.factory('ResourceFactory', ["$q", "$resource", function($q, $resource) {
function createResource(url, options, actions) {
var actions = actions || {},
resource,
outstanding = [];
Object.keys(actions).forEach(function (action) {
console.log(actions[action]);
var canceller = $q.defer();
actions[action].timeout = canceller.promise;
actions[action].Canceller = canceller;
});
resource = $resource(url, options, actions);
Object.keys(actions).forEach(function (action) {
var method = resource[action];
resource[action] = function () {
var deferred = $q.defer(),
promise = method.apply(null, arguments).$promise;
abortablePromiseWrap(promise, deferred, outstanding);
return {
promise: deferred.promise,
abort: function () {
deferred.reject('Aborted');
},
cancel: function () {
console.log(actions[action]);
actions[action].Canceller.resolve("Call cancelled");
}
};
};
});
/**
* Abort all the outstanding requests on
* this $resource. Calls promise.reject() on outstanding [].
*/
resource.abortAll = function () {
for (var i = 0; i < outstanding.length; i++) {
outstanding[i].reject('Aborted all');
}
outstanding = [];
};
return resource;
}
return {
createResource: function (url, options, actions) {
return createResource(url, options, actions);
}
}
}])
function abortablePromiseWrap(promise, deferred, outstanding) {
promise.then(function () {
deferred.resolve.apply(deferred, arguments);
});
promise.catch(function () {
deferred.reject.apply(deferred, arguments);
});
/**
* Remove from the outstanding array
* on abort when deferred is rejected
* and/or promise is resolved/rejected.
*/
deferred.promise.finally(function () {
array.remove(outstanding, deferred);
});
outstanding.Push(deferred);
}
//Usage SERVICE
factory("ServiceFactory", ["apiBasePath", "$resource", "ResourceFactory", function (apiBasePath, $resource, QiteResourceFactory) {
return ResourceFactory.createResource(apiBasePath + "service/:id", { id: '@id' }, null);
}])
//Usage Controller
var result = ServiceFactory.get();
console.log(result);
result.promise.then(function (data) {
$scope.services = data;
}).catch(function (a) {
console.log("catch", a);
})
//Actually cancels xhr request
result.cancel();
Une solution consiste à recréer la ressource chaque fois que vous en avez besoin.
// for canceling an ajax request
$scope.canceler = $q.defer();
// create a resource
// (we have to re-craete it every time because this is the only
// way to renew the promise)
function getAPI(promise) {
return $resource(
'index.html', {}, {
get: {
method: 'GET',
timeout: promise
}
}
);
}
$scope.fetchData = function() {
// abort previous requests if they are still running
$scope.canceler.resolve();
// create a new canceler
$scope.canceler = $q.defer();
// instead of using "API.get" we use "getAPI().get"
getAPI( $scope.canceler.promise ).get({}, function() {
$scope.actions.Push('completed');
$scope.pending = 0;
}, function() {
$scope.actions.Push('aborted');
});
}
Dans notre tentative de résoudre cette tâche, nous sommes arrivés à la solution suivante:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Cancel resource</title>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.9/angular.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.9/angular-resource.js"></script>
<script>
angular.module("app", ["ngResource"]).
factory(
"services",
["$resource", function($resource)
{
function resolveAction(resolve)
{
if (this.params)
{
this.timeout = this.params.timeout;
this.params.timeout = null;
}
this.then = null;
resolve(this);
}
return $resource(
"http://md5.jsontest.com/",
{},
{
MD5:
{
method: "GET",
params: { text: null },
then: resolveAction
},
});
}]).
controller(
"Test",
["services", "$q", "$timeout", function(services, $q, $timeout)
{
this.value = "Sample text";
this.requestTimeout = 100;
this.call = function()
{
var self = this;
self.result = services.MD5(
{
text: self.value,
timeout: $q(function(resolve)
{
$timeout(resolve, self.requestTimeout);
})
});
}
}]);
</script>
</head>
<body ng-app="app" ng-controller="Test as test">
<label>Text: <input type="text" ng-model="test.value" /></label><br/>
<label>Timeout: <input type="text" ng-model="test.requestTimeout" /></label><br/>
<input type="button" value="call" ng-click="test.call()"/>
<div ng-bind="test.result.md5"></div>
</body>
</html>
Veuillez consulter " Annuler la demande de ressources Angularjs " pour plus de détails.