rutas - ¿Puedes cambiar una ruta sin tener que volver a cargar el controlador en AngularJS?
rutas angularjs (9)
¿por qué no simplemente poner el controlador ng un nivel más alto,
<body ng-controller="ProjectController">
<div ng-view><div>
Y no configure el controlador en la ruta,
.when(''/'', { templateUrl: "abc.html" })
esto funciona para mi.
Se ha preguntado antes, y de las respuestas no se ve bien. Me gustaría preguntar con este código de muestra en consideración ...
Mi aplicación carga el elemento actual en el servicio que lo proporciona. Hay varios controladores que manipulan los datos del artículo sin que el artículo se vuelva a cargar.
Mis controladores volverán a cargar el artículo si aún no está configurado, de lo contrario, usará el elemento cargado actualmente del servicio, entre los controladores.
Problema : me gustaría utilizar diferentes rutas para cada controlador sin volver a cargar Item.html.
1) ¿Es eso posible?
2) Si eso no es posible, ¿hay un mejor enfoque para tener un camino por controlador frente a lo que se me ocurrió aquí?
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);
}
Resolución.
Estado : Usar la consulta de búsqueda como se recomienda en la respuesta aceptada me permitió proporcionar diferentes URL sin volver a cargar el controlador 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'');
}
});
}
});
El contenido dentro de mis parciales está envuelto con ng-controller=...
Agregar la siguiente etiqueta de cabecera
<script type="text/javascript">
angular.element(document.getElementsByTagName(''head'')).append(angular.element(''<base href="'' + window.location.pathname + ''" />''));
</script>
Esto evitará la recarga.
Aquí está mi solución más completa que resuelve algunas cosas que @Vigrond y @rahilwazir se perdieron:
- Cuando se cambiaban los parámetros de búsqueda, impedía la transmisión de
$routeUpdate
. - Cuando la ruta no se modifica,
$locationChangeSuccess
nunca se activa, lo que hace que se evite la próxima actualización de la ruta. Si en el mismo ciclo de resumen había otra solicitud de actualización, esta vez si deseaba volver a cargar, el controlador de eventos cancelaría esa recarga.
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); }; }); }]);
Aunque esta publicación es antigua y se ha aceptado una respuesta, el uso de reloadOnSeach = false no resuelve el problema para aquellos de nosotros que necesitamos cambiar la ruta real y no solo los params. Aquí hay una solución simple a considerar:
Use ng-include en lugar de ng-view y asigne su controlador en la plantilla.
<!-- 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
})
El único inconveniente es que no se puede usar el atributo de resolución, pero es bastante fácil de usar. También debe gestionar el estado del controlador, como la lógica basada en $ routeParams a medida que la ruta cambia dentro del controlador a medida que cambia la url correspondiente.
Aquí hay un ejemplo: http://plnkr.co/edit/WtAOm59CFcjafMmxBVOP?p=preview
Las respuestas anteriores, incluida la de GitHub, tenían algunos problemas para mi escenario y también el botón Atrás o el cambio de URL directo del navegador recargaba el controlador, lo que no me gustó. Finalmente fui con el siguiente enfoque:
1. Defina una propiedad en las definiciones de ruta, llamada ''noReload'' para aquellas rutas donde no desea que el controlador vuelva a cargar en el cambio de ruta.
.when(''/:param1/:param2?/:param3?'', {
templateUrl: ''home.html'',
controller: ''HomeController'',
controllerAs: ''vm'',
noReload: true
})
2. En la función de ejecución de su módulo, ponga la lógica que verifica esas rutas. noReload
volver a cargar solo si noReload
es true
y el controlador de ruta anterior es el mismo.
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. Finalmente, en el controlador, escuche el evento $ routeUpdate para que pueda hacer lo que necesite hacer cuando cambien los parámetros de la ruta.
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
});
//(...)
}
Tenga en cuenta que con este enfoque, no cambia las cosas en el controlador y luego actualiza la ruta en consecuencia. Es al revés. Primero cambie la ruta, luego escuche el evento $ routeUpdate para cambiar las cosas en el controlador cuando cambien los parámetros de la ruta.
Esto mantiene las cosas simples y consistentes, ya que puede usar la misma lógica tanto cuando simplemente cambia la ruta (pero sin costosas solicitudes $ http, si lo desea) como cuando recarga completamente el navegador.
Para aquellos que necesitan el cambio de ruta () sin controladores, recarga: aquí está el complemento: https://github.com/anglibs/angular-location-update
Uso:
$location.update_path(''/notes/1'');
Basado en https://.com/a/24102139/1751321
PS Esta solución https://.com/a/24102139/1751321 contiene un error después de la ruta (, falsa) llamada - se interrumpirá la navegación del navegador hacia atrás / adelante hasta que se llame a path (, true)
Si necesita cambiar la ruta, agregue esto después de .config en su archivo de aplicación. Entonces puede hacer $location.path(''/sampleurl'', false);
para evitar la recarga
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]);
};
}])
El crédito va a https://www.consolelog.io/angularjs-change-path-without-reloading por la solución más elegante que he encontrado.
Si no tiene que usar URL como #/item/{{item.id}}/foo
y #/item/{{item.id}}/bar
pero #/item/{{item.id}}/?foo
y #/item/{{item.id}}/?bar
lugar, puede configurar su ruta para /item/{{item.id}}/
para que reloadOnSearch
establezca como false
( docs.angularjs.org/api/ngRoute.$routeProvider ). Eso le dice a AngularJS que no vuelva a cargar la vista si la parte de búsqueda de la url cambia.
Yo uso esta solución
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;
});
});
Este enfoque conserva la funcionalidad del botón de retroceso, y siempre tiene los parámetros de ruta actuales en la plantilla, a diferencia de algunos otros enfoques que he visto.