Cela a déjà été demandé, et d'après les réponses, ça n'a pas l'air bien. Je voudrais demander avec cet exemple de code en considération ...
Mon application charge l'élément en cours dans le service qui le fournit. Il existe plusieurs contrôleurs qui manipulent les données d'élément sans que l'élément ne soit rechargé.
Mes contrôleurs rechargeront l'élément s'il n'est pas encore défini. Sinon, il utilisera l'élément actuellement chargé du service entre les contrôleurs.
Problème : Je voudrais utiliser des chemins différents pour chaque contrôleur sans recharger Item.html.
1) Est-ce possible?
2) Si cela n’est pas possible, existe-t-il une meilleure approche pour avoir un chemin par contrôleur par rapport à ce que j’ai proposé ici?
app.js
var app = angular.module('myModule', []).
config(['$routeProvider', function($routeProvider) {
$routeProvider.
when('/items', {templateUrl: 'partials/items.html', controller: ItemsCtrl}).
when('/items/:itemId/foo', {templateUrl: 'partials/item.html', controller: ItemFooCtrl}).
when('/items/:itemId/bar', {templateUrl: 'partials/item.html', controller: ItemBarCtrl}).
otherwise({redirectTo: '/items'});
}]);
Item.html
<!-- Menu -->
<a id="fooTab" my-active-directive="view.name" href="#/item/{{item.id}}/foo">Foo</a>
<a id="barTab" my-active-directive="view.name" href="#/item/{{item.id}}/bar">Bar</a>
<!-- Content -->
<div class="content" ng-include="" src="view.template"></div>
controller.js
// Helper function to load $scope.item if refresh or directly linked
function itemCtrlInit($scope, $routeParams, MyService) {
$scope.item = MyService.currentItem;
if (!$scope.item) {
MyService.currentItem = MyService.get({itemId: $routeParams.itemId});
$scope.item = MyService.currentItem;
}
}
function itemFooCtrl($scope, $routeParams, MyService) {
$scope.view = {name: 'foo', template: 'partials/itemFoo.html'};
itemCtrlInit($scope, $routeParams, MyService);
}
function itemBarCtrl($scope, $routeParams, MyService) {
$scope.view = {name: 'bar', template: 'partials/itemBar.html'};
itemCtrlInit($scope, $routeParams, MyService);
}
Résolution.
Status : Utiliser la requête de recherche comme recommandé dans la réponse acceptée me permettait de fournir différentes URL sans recharger le contrôleur principal.
app.js
var app = angular.module('myModule', []).
config(['$routeProvider', function($routeProvider) {
$routeProvider.
when('/items', {templateUrl: 'partials/items.html', controller: ItemsCtrl}).
when('/item/:itemId/', {templateUrl: 'partials/item.html', controller: ItemCtrl, reloadOnSearch: false}).
otherwise({redirectTo: '/items'});
}]);
Item.html
<!-- Menu -->
<dd id="fooTab" item-tab="view.name" ng-click="view = views.foo;"><a href="#/item/{{item.id}}/?view=foo">Foo</a></dd>
<dd id="barTab" item-tab="view.name" ng-click="view = views.bar;"><a href="#/item/{{item.id}}/?view=foo">Bar</a></dd>
<!-- Content -->
<div class="content" ng-include="" src="view.template"></div>
controller.js
function ItemCtrl($scope, $routeParams, Appts) {
$scope.views = {
foo: {name: 'foo', template: 'partials/itemFoo.html'},
bar: {name: 'bar', template: 'partials/itemBar.html'},
}
$scope.view = $scope.views[$routeParams.view];
}
directives.js
app.directive('itemTab', function(){
return function(scope, elem, attrs) {
scope.$watch(attrs.itemTab, function(val) {
if (val+'Tab' == attrs.id) {
elem.addClass('active');
} else {
elem.removeClass('active');
}
});
}
});
Le contenu de mes partiels est entouré de ng-controller=...
Si vous ne devez pas utiliser d'URL telles que #/item/{{item.id}}/foo
et #/item/{{item.id}}/bar
mais plutôt #/item/{{item.id}}/?foo
et #/item/{{item.id}}/?bar
, vous pouvez configurer votre route pour /item/{{item.id}}/
afin que reloadOnSearch
soit défini sur false
( docs.angularjs.org/api/ngRoute.$routeProvider ). Cela indique à AngularJS de ne pas recharger la vue si la partie de recherche de l’URL change.
Si vous devez modifier le chemin, ajoutez-le après votre fichier .config dans votre fichier d'application . Ensuite, vous pouvez faire $location.path('/sampleurl', false);
pour empêcher le rechargement
app.run(['$route', '$rootScope', '$location', function ($route, $rootScope, $location) {
var original = $location.path;
$location.path = function (path, reload) {
if (reload === false) {
var lastRoute = $route.current;
var un = $rootScope.$on('$locationChangeSuccess', function () {
$route.current = lastRoute;
un();
});
}
return original.apply($location, [path]);
};
}])
Le crédit va à https://www.consolelog.io/angularjs-change-path-without-reloading pour la solution la plus élégante que j'ai trouvée.
pourquoi ne pas simplement placer le contrôleur ng à un niveau supérieur,
<body ng-controller="ProjectController">
<div ng-view><div>
Et ne pas mettre le contrôleur dans la route,
.when('/', { templateUrl: "abc.html" })
ça marche pour moi.
Pour ceux qui ont besoin de path () change sans contrôleurs reload -Here est plugin: https://github.com/anglibs/angular-location-update
Usage:
$location.update_path('/notes/1');
Basé sur https://stackoverflow.com/a/24102139/1751321
P.S. Cette solution https://stackoverflow.com/a/24102139/1751321 contient le bogue Après le chemin (, false) appelé - la navigation dans le navigateur sera interrompue jusqu'à ce que le chemin (, true) soit appelé.
Bien que ce message soit ancien et qu’une réponse ait été acceptée, l’utilisation de reloadOnSeach = false ne résout pas le problème pour ceux d’entre nous qui ont besoin de changer de chemin réel et pas seulement les paramètres. Voici une solution simple à considérer:
Utilisez ng-include au lieu de ng-view et assignez votre contrôleur dans le modèle.
<!-- In your index.html - instead of using ng-view -->
<div ng-include="templateUrl"></div>
<!-- In your template specified by app.config -->
<div ng-controller="MyController">{{variableInMyController}}</div>
//in config
$routeProvider
.when('/my/page/route/:id', {
templateUrl: 'myPage.html',
})
//in top level controller with $route injected
$scope.templateUrl = ''
$scope.$on('$routeChangeSuccess',function(){
$scope.templateUrl = $route.current.templateUrl;
})
//in controller that doesn't reload
$scope.$on('$routeChangeSuccess',function(){
//update your scope based on new $routeParams
})
Seul inconvénient, vous ne pouvez pas utiliser l'attribut resol, mais c'est assez facile à utiliser. Vous devez également gérer l’état du contrôleur, comme une logique basée sur $ routeParams lorsque la route change dans le contrôleur lorsque l’URL correspondante change.
Voici un exemple: http://plnkr.co/edit/WtAOm59CFcjafMmxBVOP?p=preview
J'utilise cette solution
angular.module('reload-service.module', [])
.factory('reloadService', function($route,$timeout, $location) {
return {
preventReload: function($scope, navigateCallback) {
var lastRoute = $route.current;
$scope.$on('$locationChangeSuccess', function() {
if (lastRoute.$$route.templateUrl === $route.current.$$route.templateUrl) {
var routeParams = angular.copy($route.current.params);
$route.current = lastRoute;
navigateCallback(routeParams);
}
});
}
};
})
//usage
.controller('noReloadController', function($scope, $routeParams, reloadService) {
$scope.routeParams = $routeParams;
reloadService.preventReload($scope, function(newParams) {
$scope.routeParams = newParams;
});
});
Cette approche préserve la fonctionnalité du bouton Précédent et vous avez toujours les routeParams actuels dans le modèle, contrairement à d'autres approches que j'ai déjà vues.
Voici ma solution plus complète qui résout quelques problèmes que @Vigrond et @rahilwazir ont manqués:
$routeUpdate
.$locationChangeSuccess
n'est jamais déclenché, ce qui empêche la mise à jour de la route next route.Si, dans le même cycle de résumé, il y avait une autre demande de mise à jour, cette fois pour recharger, le gestionnaire d'événements annulerait ce rechargement.
app.run(['$rootScope', '$route', '$location', '$timeout', function ($rootScope, $route, $location, $timeout) {
['url', 'path'].forEach(function (method) {
var original = $location[method];
var requestId = 0;
$location[method] = function (param, reload) {
// getter
if (!param) return original.call($location);
# only last call allowed to do things in one digest cycle
var currentRequestId = ++requestId;
if (reload === false) {
var lastRoute = $route.current;
// intercept ONLY the next $locateChangeSuccess
var un = $rootScope.$on('$locationChangeSuccess', function () {
un();
if (requestId !== currentRequestId) return;
if (!angular.equals($route.current.params, lastRoute.params)) {
// this should always be broadcast when params change
$rootScope.$broadcast('$routeUpdate');
}
var current = $route.current;
$route.current = lastRoute;
// make a route change to the previous route work
$timeout(function() {
if (requestId !== currentRequestId) return;
$route.current = current;
});
});
// if it didn't fire for some reason, don't intercept the next one
$timeout(un);
}
return original.call($location, param);
};
});
}]);
Ajouter la balise suivante dans la tête
<script type="text/javascript">
angular.element(document.getElementsByTagName('head')).append(angular.element('<base href="' + window.location.pathname + '" />'));
</script>
Cela empêchera le rechargement.
Les réponses ci-dessus, y compris celle de GitHub, présentaient quelques problèmes pour mon scénario et le bouton de retour ou le changement direct d'URL à partir du navigateur rechargeait le contrôleur, ce que je n'aimais pas. Je suis finalement allé avec l'approche suivante:
1. Définissez une propriété dans les définitions de route, appelée "noReload" pour les routes pour lesquelles vous ne souhaitez pas que le contrôleur se recharge lors du changement de route.
.when('/:param1/:param2?/:param3?', {
templateUrl: 'home.html',
controller: 'HomeController',
controllerAs: 'vm',
noReload: true
})
2. Dans la fonction d'exécution de votre module, insérez la logique qui vérifie ces itinéraires. Cela empêchera le rechargement uniquement si noReload
est true
et que le contrôleur de route précédent est identique.
fooRun.$inject = ['$rootScope', '$route', '$routeParams'];
function fooRun($rootScope, $route, $routeParams) {
$rootScope.$on('$routeChangeStart', function (event, nextRoute, lastRoute) {
if (lastRoute && nextRoute.noReload
&& lastRoute.controller === nextRoute.controller) {
var un = $rootScope.$on('$locationChangeSuccess', function () {
un();
// Broadcast routeUpdate if params changed. Also update
// $routeParams accordingly
if (!angular.equals($route.current.params, lastRoute.params)) {
lastRoute.params = nextRoute.params;
angular.copy(lastRoute.params, $routeParams);
$rootScope.$broadcast('$routeUpdate', lastRoute);
}
// Prevent reload of controller by setting current
// route to the previous one.
$route.current = lastRoute;
});
}
});
}
3. Enfin, dans le contrôleur, écoutez l'événement $ routeUpdate afin que vous puissiez faire ce que vous devez faire lorsque les paramètres de route changent.
HomeController.$inject = ['$scope', '$routeParams'];
function HomeController($scope, $routeParams) {
//(...)
$scope.$on("$routeUpdate", function handler(route) {
// Do whatever you need to do with new $routeParams
// You can also access the route from the parameter passed
// to the event
});
//(...)
}
Gardez à l'esprit qu'avec cette approche, vous ne modifiez pas les choses dans le contrôleur, puis mettez à jour le chemin en conséquence. C'est l'inverse. Vous modifiez d’abord le chemin, puis écoutez l’événement $ routeUpdate pour modifier les paramètres du contrôleur lorsque les paramètres de route changent.
Cela permet de garder les choses simples et cohérentes, car vous pouvez utiliser la même logique lorsque vous changez simplement de chemin (mais sans demandes coûteuses $ http si vous le souhaitez) et lorsque vous rechargez complètement le navigateur.
Il y a un moyen simple de changer de chemin sans recharger
URL is - http://localhost:9000/#/edit_draft_inbox/1457
Utilisez ce code pour changer l'URL, la page ne sera pas redirigée
Le deuxième paramètre "false" est très important.
$location.path('/edit_draft_inbox/'+id, false);
Depuis la version 1.2, vous pouvez utiliser $ location.replace () :
$location.path('/items');
$location.replace();