J'ai un contrôleur, avec un itinéraire comme celui-ci:
#/articles/1234
Je souhaite modifier l'itinéraire sans recharger complètement le contrôleur, afin de pouvoir conserver la position des autres éléments dans le contrôleur (listes défilant)
Je peux penser à plusieurs façons de faire cela, mais ils sont tous très laids. Existe-t-il une meilleure pratique pour faire quelque chose comme ça? J'ai essayé d'expérimenter reloadOnSearch: false, mais cela ne semble rien changer.
Avait le même défi,
J'ai trouvé un hack dans une autre réponse StackOverflow qui a fait l'affaire
Solution assez propre - tout ce que j'ai fait est d'ajouter ces lignes au contrôleur qui définit $ location.path:
var lastRoute = $route.current;
$scope.$on('$locationChangeSuccess', function(event) {
$route.current = lastRoute;
});
..et fait en sorte que $ route soit injectée dans le contrôleur bien sûr.
Mais toujours, se sent comme "DoNotFollowRoutesOnPathChange" est une fonctionnalité manquante dans AngularJS.
/ Jens
Update: Etant donné que l'écoute de cet événement tue efficacement l'utilisation ultérieure de la configuration de $ routeProvider, j'ai dû limiter cette capture au chemin actuel:
var lastRoute = $route.current;
if ($route.current.$route.templateUrl.indexOf('mycurrentpath') > 0) {
$route.current = lastRoute;
}
Devenir laide ...
Vous pouvez utiliser la méthode $location.search()
comme vous l'avez mentionné. Vous devriez écouter l'événement "$routeUpdate"
sur l'étendue plutôt que d'autres événements de route. $ route API .
Tout d’abord (vous le savez déjà), ajoutez reloadOnSearch: false
à votre $routeProvider
:
$routeProvider.when('/somewhere', {
controller: 'SomeCtrl',
reloadOnSearch: false
})
Remplacez votre balise d'ancrage href
ou ng-href
par href="#/somewhere?param=value"
. Cela déclenchera l'événement $routeChangeSuccess
si la partie du chemin d'accès (/somewhere
) est différente de l'emplacement actuel. Sinon, l'événement $routeUpdate
sera déclenché.
Écoutez l'événement sur la portée:
$scope.$on("$routeUpdate", function(event, route) {
// some code here
});
Si vous souhaitez modifier les paramètres de recherche dans le code, vous pouvez utiliser la méthode $location.search()
. $ location.search API .
Si vous définissez reloadOnSearch sur false, vous pouvez définir la partie ?a=b&c=d
de l'URL sans recharger. Vous ne pouvez pas changer joliment l'emplacement réel, cependant, sans un rechargement.
Meilleure approche lors de l’utilisation de ngRoute:
/**
* Add skipReload() to $location service.
*
* See https://github.com/angular/angular.js/issues/1699
*/
app.factory('location',
['$rootScope', '$route', '$location',
function($rootScope, $route, $location) {
$location.skipReload = function() {
var lastRoute = $route.current;
var deregister = $rootScope.$on('$locationChangeSuccess',
function(e, absUrl, oldUrl) {
console.log('location.skipReload', 'absUrl:', absUrl, 'oldUrl:', oldUrl);
$route.current = lastRoute;
deregister();
});
return $location;
};
return $location;
}]);
Comment utiliser:
app.controller('MyCtrl', ['$scope', 'location', function($scope, location) {
$scope.submit = function() {
location.skipReload().path(path);
};
}]);
J'ai écrit une usine réutilisable pour celle basée sur la réponse de Jens X Augustsson:
app.factory('DoNotReloadCurrentTemplate', ['$route', function($route) {
return function(scope) {
var lastRoute = $route.current;
scope.$on('$locationChangeSuccess', function() {
if (lastRoute.$$route.templateUrl === $route.current.$$route.templateUrl) {
console.log('DoNotReloadCurrentTemplate',
$route.current.$$route.templateUrl);
$route.current = lastRoute;
}
});
};
}]);
Fonctionne avec AngularJS 1.0.6
Comment utiliser:
app.controller('MyCtrl',
['$scope', 'DoNotReloadCurrentTemplate',
function($scope, DoNotReloadCurrentTemplate) {
DoNotReloadCurrentTemplate($scope);
}]);
AngularJS issue ici: https://github.com/angular/angular.js/issues/1699
Utilisation:
$routeProvider.when('/somewhere', {
controller: 'SomeCtrl',
reloadOnSearch: false
})
Cela empêchera le rechargement du contrôleur lors de la modification du paramètre de requête, mais également lors du changement de hachage.
définir une fabrique pour gérer window.history de HTML5 comme suit (notez que je garde une pile pour la faire fonctionner sur Android également car elle présente quelques problèmes):
.factory('History', function($rootScope) {
var StateQueue = [];
var StatePointer = 0;
var State = undefined;
window.onpopstate = function(){
// called when back button is pressed
State = StateQueue.pop();
State = (State)?State:{};
StatePointer = (StatePointer)?StatePointer-1:0;
$rootScope.$broadcast('historyStateChanged', State);
window.onpopstate = window.onpopstate;
}
return {
replaceState : function(data, title, url) {
// replace current state
var State = this.state();
State = {state : data};
window.history.replaceState(State,title,url);
StateQueue[StatePointer] = State;
$rootScope.$broadcast('historyStateChanged', this.state());
},
pushState : function(data, title, url){
// Push state and increase pointer
var State = this.state();
State = {state : data};
window.history.pushState(State,title,url);
StateQueue.Push(State);
$rootScope.$broadcast('historyStateChanged', this.state());
StatePointer ++;
},
fakePush : function(data, title, url) {
// call this when you do location.url(url)
StateQueue.Push((StateQueue.length - 1 >= 0)?StateQueue[StateQueue.length -1]:{});
StatePointer ++;
$rootScope.$broadcast('historyStateChanged', this.state());
},
state : function() {
// get current state
return (StateQueue[StatePointer])?StateQueue[StatePointer]:{};
},
popState : function(data) {
// TODO manually popState
},
back : function(data) {
// TODO needed for iphone support since iphone doesnt have a back button
}
}
})
ajoutez quelques auditeurs sur vos champs dépendants et vous irez bien ainsi:
$scope.$on('historyStateChanged', function(e,v) {
if(v)
$scope.state = v;
else
$scope.state = {}
});
c'est comme ça que je le fais. De mon point de vue, l'URL ne devrait changer que lorsqu'une nouvelle vue est chargée. Je pense que c’est ce que l’équipe angulaire avait prévu quand même. Si vous souhaitez mapper le bouton suivant/précédent sur votre modèle, essayez de le faire avec la fenêtre de HTML5.
J'espère que je pourrais aider . À la vôtre, Heinrich
Si vous atterrissez ici en 2015: La vraie solution ici est de n’utiliser aucun de ces hacks (j’ose les nommer ainsi, car en utilisant l’une des méthodes énumérées ci-dessus, vous perdrez la possibilité d’utiliser résolution et autres comme ui-router .
Voici une présentation pratique sur les différences. L'implémentation devrait être aussi simple que d'échanger $ route pour $ state et de convertir les états en noms.
Je suis actuellement en train de passer à une méthode dans laquelle je vais me référer avec un href à une route, avec un paramètre optionnel get qui change l’état sans le recharger. Pour plus d'informations à ce sujet, consultez la section 'params' ici
Vous pouvez le faire sans les hacks $locationChange~
et HistoryState
en utilisant l'option route
s resolve
promise.
En supposant que vous ayez un itinéraire article
où ce nombre est ce qui a changé, vous pouvez le faire;
$routeProvider.when(
'/article/:number',
{
templateUrl : 'partial.html',
controller : 'ArticleCtrl',
resolve : {
load : ['$q', '$routeParams', function($q, $routeParams) {
var defer = $q.defer();
//number was specified in the previous route parameters, means we were already on the article page
if ($routeParams.number)
//so dont reload the view
defer.reject('');
//otherwise, the number argument was missing, we came to this location from another route, load the view
else
defer.resolve();
return defer.promise;
}]
}
}
);
Cette solution simple (étonnamment) fonctionne pour moi:
$state.params.id = id; //updating the $state.params somehow prevents reloading the state
$location.path('/articles/' + id);
Cela n'empêche pas le rechargement de l'état sur les boutons Précédent et Suivant cependant ... Note: j'utilise angularjs 1.2.7 et ui-router 0.0.1 (je sais que c'est vieux).
Voici le plugin: https://github.com/anglibs/angular-location-update
Usage:
$location.update_path('/notes/1');