195

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

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

如果尚未设置,我的控制器将重新加载该项目,否则,它将在控制器之间使用服务中当前加载的项目。

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

1)这可能吗?

2)如果这是不可能的,有没有更好的方法让每个控制器都有一个路径而不是我在这里想出的?

应用程序.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'});
    }]);

项目.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>

控制器.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);
}

解析度。

状态:使用接受的答案中推荐的搜索查询允许我提供不同的 url,而无需重新加载主控制器。

应用程序.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'});
    }]);

项目.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>

控制器.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];
}

指令.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=...

4

12 回答 12

120

如果您不必使用#/item/{{item.id}}/fooand #/item/{{item.id}}/barbut #/item/{{item.id}}/?fooand之类的 URL,则可以将#/item/{{item.id}}/?bar路由设置为( https://docs.angularjs.org/api/ngRoute/provider/$routeProvider )。如果 url 的搜索部分发生变化,这会告诉 AngularJS 不要重新加载视图。/item/{{item.id}}/reloadOnSearchfalse

于 2013-02-20T07:27:30.997 回答
94

如果您需要更改路径,请将其添加到应用文件中的 .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 。

于 2014-06-08T01:14:36.390 回答
18

为什么不把 ng-controller 提高一级,

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

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

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

这个对我有用。

于 2014-11-17T23:30:38.280 回答
13

对于那些需要在不重新加载控制器的情况下更改路径()的人 - 这是插件:https ://github.com/anglibs/angular-location-update

用法:

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

基于https://stackoverflow.com/a/24102139/1751321

PS 这个解决方案https://stackoverflow.com/a/24102139/1751321在调用 path(, false) 后包含错误 - 它会中断浏览器导航后退/前进,直到调用 path(, true)

于 2015-08-12T11:31:53.700 回答
10

尽管这篇文章很旧并且已经接受了答案,但对于我们这些需要更改实际路径而不仅仅是参数的人来说,使用 reloadOnSeach=false 并不能解决问题。这是一个需要考虑的简单解决方案:

使用 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
})

唯一的缺点是您不能使用 resolve 属性,但这很容易解决。此外,您还必须管理控制器的状态,例如基于 $routeParams 的逻辑,因为控制器内的路由会随着相应 url 的变化而变化。

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

于 2013-09-16T05:31:55.240 回答
2

我使用这个解决方案

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

这种方法保留了后退按钮功能,并且您始终在模板中拥有当前的 routeParams,这与我见过的其他一些方法不同。

于 2016-04-10T00:17:00.603 回答
1

上面的答案,包括 GitHub 答案,对我的场景有一些问题,并且后退按钮或浏览器的直接 url 更改正在重新加载控制器,我不喜欢。我最终采用了以下方法:

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

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

2.在模块的运行函数中,放置检查这些路由的逻辑。noReload只有当istrue和之前的路由控制器相同时,它才会阻止重新加载。

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

    //(...)
}

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

这使事情变得简单和一致,因为当您简单地更改路径(但如果您愿意,无需昂贵的 $http 请求)和完全重新加载浏览器时,您都可以使用相同的逻辑。

于 2016-10-02T20:05:00.167 回答
0

这是我更完整的解决方案,它解决了@Vigrond 和@rahilwazir 错过的一些问题:

  • 当搜索参数改变时,它会阻止广播一个$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);
            };
        });
    }]);
    
于 2016-09-26T16:03:50.833 回答
0

在 head 标签内添加以下内容

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

这将防止重新加载。

于 2017-03-29T02:42:22.517 回答
0

有一种简单的方法可以更改路径而无需重新加载

URL is - http://localhost:9000/#/edit_draft_inbox/1457

使用此代码更改 URL,页面不会被重定向

第二个参数“false”非常重要。

$location.path('/edit_draft_inbox/'+id, false);
于 2018-05-17T11:47:26.700 回答
0

从 1.2 版开始,您可以使用$location.replace()

$location.path('/items');
$location.replace();
于 2018-12-11T04:32:20.330 回答
0

我无法在这里做出任何答案。作为一个可怕的黑客,我在更改路由时将时间戳存储在本地存储中,并在页面初始化时检查此时间戳是否已设置且最近,在这种情况下,我不会触发一些初始化操作。

在控制器中:

window.localStorage['routeChangeWithoutReloadTimestamp'] = new Date().getTime();
$location.path(myURL);

在配置中:

.when(myURL, {
            templateUrl: 'main.html',
            controller:'MainCtrl',
            controllerAs: 'vm',
            reloadOnSearch: false,
            resolve:
            {
                var routeChangeWithoutReloadTimestamp =
                    window.localStorage['routeChangeWithoutReloadTimestamp'];
                var currentTimestamp = new Date().getTime();
                if (!routeChangeWithoutReloadTimestamp ||
                        currentTimestamp - routeChangeWithoutReloadTimestamp >= 5000) {
                    //initialization code here
                }
                //reset the timestamp to trigger initialization when needed
                window.localStorage['routeChangeWithoutReloadTimestamp'] = 0;
            }
});

我使用了时间戳而不是布尔值,以防代码在有机会在更改路线之前重新初始化存储的值之前被中断。选项卡之间发生冲突的风险非常低。

于 2020-08-07T14:46:39.847 回答