你可以改变一个path,而不用重新加载AngularJS中的控制器?

之前有人问过,从答案来看,这看起来不太好。 我想问一下这个示例代码

我的应用程序在提供它的服务中加载当前项目。 有几个控制器可以在不重新加载项目的情况下操作项目数据。

我的控制器将重新加载项目,如果它还没有设置,否则,它将使用当前加载的项目从服务,控制器之间。

问题 :我想为每个控制器使用不同的path,而无需重新加载Item.html。

1)这可能吗?

2)如果这是不可能的,是否有一个更好的方法来使每个控制器的path与我在这里想出的?

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); } 

parsing度。

状态 :使用search查询按照接受的答案build议允许我提供不同的url,而无需重新加载主控制器。

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'); } }); } }); 

我的部分里面的内容是用ng-controller=...包装的

如果您不必使用像#/item/{{item.id}}/foo#/item/{{item.id}}/bar而是使用#/item/{{item.id}}/?foo#/item/{{item.id}}/?bar ,您可以为/item/{{item.id}}/设置路由,将reloadOnSearch设置为false( docs.angularjs.org/ api / ngRoute。$ routeProvider )。 这告诉AngularJS不重新加载视图,如果search部分的url更改。

如果您需要更改path,请在.config文件中添加。 然后你可以做$location.path('/sampleurl', false); 以防止重新加载

 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]); }; }]) 

信贷到https://www.consolelog.io/angularjs-change-path-without-reloading为我find的最优雅的解决scheme。;

为什么不把ng-controller更高一级呢?

 <body ng-controller="ProjectController"> <div ng-view><div> 

并且不要在路由中设置控制器,

 .when('/', { templateUrl: "abc.html" }) 

它适用于我。

虽然这篇文章比较老,并且已经有了答案,但是使用reloadOnSeach = false并不能解决我们这些需要改变实际path而不仅仅是参数的问题。 这里有一个简单的解决scheme要考虑:

使用ng-include而不是ng-view并在模板中分配你的控制器。

 <!-- 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 }) 

只有不好的一面是你不能使用parsing属性,但这很容易解决。 你也必须pipe理控制器的状态,就像基于$ routeParams的逻辑一样,因为控制器内的路由改变了,因为相应的url改变了。

这是一个例子: http : //plnkr.co/edit/WtAOm59CFcjafMmxBVOP?p=preview

对于那些需要path()更改没有控制器重新加载 – 这里是插件: https : //github.com/anglibs/angular-location-update

用法:

 $location.update_path('/notes/1'); 

根据https://stackoverflow.com/a/24102139/1751321

PS这个解决schemehttps://stackoverflow.com/a/24102139/1751321包含错误的path(,假)调用 – 它将打破浏览器导航回/转发,直到path(,真)被称为

我使用这个解决scheme

 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; }); }); 

这种方法保留了后退button的function,而且在模板中总是有当前的routeParams,这与我所见过的其他一些方法不同。

上面的答案,包括GitHub之一,有一些问题,我的scheme也回来的button或从浏览器直接url更改重新加载控制器,我不喜欢。 我终于用了以下的方法:

1.在路由定义中定义一个属性,称为“noReload”,用于那些不希望控制器在路由更改时重新加载的路由。

 .when('/:param1/:param2?/:param3?', { templateUrl: 'home.html', controller: 'HomeController', controllerAs: 'vm', noReload: true }) 

2.在你的模块的运行function,把检查这些路线的逻辑。 只有在noReloadtrue并且以前的路由控制器是相同的情况下才会阻止重新加载。

 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.最后,在控制器中,听取$ routeUpdate事件,以便在路由参数更改时执行您所需的任何操作。

 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 }); //(...) } 

请记住,使用这种方法,您不会更改控制器中的内容,然后相应地更新path。 这是相反的方式。 您首先更改path,然后在路由参数更改时监听$ routeUpdate事件以更改控制器中的内容。

这使得事情变得简单和一致,因为当你简单地改变path(但是如果你愿意的话不需要昂贵的$ http请求)和完全重载浏览器的时候,你可以使用相同的逻辑。

这是我的更全面的解决scheme,它解决了一些@Vigrond和@rahilwazir错过的事情:

  • 当search参数被改变时,它将阻止广播$routeUpdate
  • 当路线实际上保持不变时, $locationChangeSuccess从不被触发,导致下一次路线更新被阻止。
  • 如果在相同的摘要周期中有另一个更新请求,这次希望重新加载,事件处理程序将取消该重新加载。

     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); }; }); }]); 

添加以下头标签

  <script type="text/javascript"> angular.element(document.getElementsByTagName('head')).append(angular.element('<base href="' + window.location.pathname + '" />')); </script> 

这将防止重新加载。