你可以改变一个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,把检查这些路线的逻辑。 只有在noReload
为true
并且以前的路由控制器是相同的情况下才会阻止重新加载。
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>
这将防止重新加载。