35

我有一个控制器,路线如下:

#/articles/1234

我想在不完全重新加载控制器的情况下更改路线,所以我可以保持控制器中其他东西的位置不变(滚动的列表)

我可以想到几种方法来做到这一点,但它们都很丑陋。有没有做这样的事情的最佳实践?我尝试尝试使用 reloadOnSearch: false,但它似乎并没有改变任何东西。

4

10 回答 10

34

有同样的挑战,

在另一个 StackOverflow 响应中发现了一个可以解决问题的 hack

相当干净的解决方案 - 我所做的只是将这些行添加到设置 $location.path 的控制器中:

var lastRoute = $route.current;
$scope.$on('$locationChangeSuccess', function(event) {
    $route.current = lastRoute;
});

..并确保将 $route 注入到控制器中。

但是,感觉就像“DoNotFollowRoutesOnPathChange”是AngularJS中缺少的功能。

/詹斯

更新:由于监听此事件有效地杀死了 $routeProvider 配置的进一步使用,我不得不将此捕获限制为仅当前路径:

    var lastRoute = $route.current;
    if ($route.current.$route.templateUrl.indexOf('mycurrentpath') > 0) {
        $route.current = lastRoute;         
    }

变丑了...

于 2013-01-15T01:03:16.610 回答
21

简要回答:

您可以使用$location.search()您提到的方法。您应该在范围内收听"$routeUpdate"事件,而不是其他路由事件。$路由 API

解释:

  1. 首先(您已经知道),添加reloadOnSearch: false到您的$routeProvider

    $routeProvider.when('/somewhere', {
        controller: 'SomeCtrl',
        reloadOnSearch: false
    })
    
  2. 如果路径部分 ( ) 与当前位置不同,则更改您的锚标记href或这将触发事件。否则会触发事件。ng-hrefhref="#/somewhere?param=value"$routeChangeSuccess/somewhere$routeUpdate

  3. 监听范围内的事件:

    $scope.$on("$routeUpdate", function(event, route) {
        // some code here
    });
    
  4. 如果要更改代码中的搜索参数,可以使用$location.search()方法。$location.search API

于 2013-07-12T03:10:32.387 回答
8

如果将 reloadOnSearch 设置为 false,则可以设置?a=b&c=d不重新加载的 url 部分。但是,如果不重新加载,您将无法漂亮地更改实际位置。

于 2012-08-25T03:05:05.673 回答
7

编辑

使用 ngRoute 时更好的方法:

/**
 * Add skipReload() to $location service.
 *
 * See https://github.com/angular/angular.js/issues/1699
 */
app.factory('location',
  ['$rootScope', '$route', '$location',
  function($rootScope, $route, $location) {

  $location.skipReload = function() {
    var lastRoute = $route.current;

    var deregister = $rootScope.$on('$locationChangeSuccess',
                                    function(e, absUrl, oldUrl) {
      console.log('location.skipReload', 'absUrl:', absUrl, 'oldUrl:', oldUrl);
      $route.current = lastRoute;
      deregister();
    });

    return $location;
  };

  return $location;
}]);

如何使用:

app.controller('MyCtrl', ['$scope', 'location', function($scope, location) {
  $scope.submit = function() {
    location.skipReload().path(path);
  };
}]);

旧答案

我根据 Jens X Augustsson 的回答写了一个可重用的工厂:

app.factory('DoNotReloadCurrentTemplate', ['$route', function($route) {
  return function(scope) {
    var lastRoute = $route.current;
    scope.$on('$locationChangeSuccess', function() {
      if (lastRoute.$$route.templateUrl === $route.current.$$route.templateUrl) {
        console.log('DoNotReloadCurrentTemplate',
                    $route.current.$$route.templateUrl);
        $route.current = lastRoute;
      }
    });
  };
}]);

适用于 AngularJS 1.0.6

如何使用:

app.controller('MyCtrl',
  ['$scope', 'DoNotReloadCurrentTemplate',
  function($scope, DoNotReloadCurrentTemplate) {

  DoNotReloadCurrentTemplate($scope);
}]);

AngularJS 问题在这里:https ://github.com/angular/angular.js/issues/1699

于 2013-05-11T10:24:23.097 回答
1

定义一个工厂来处理 HTML5 的 window.history 像这样(注意我保留了一个自己的堆栈以使其在 android 上也能工作,因为它在那里有一些问题):

.factory('History', function($rootScope) {
    var StateQueue = [];
    var StatePointer = 0;
    var State = undefined;
    window.onpopstate = function(){
        // called when back button is pressed
        State = StateQueue.pop();
        State = (State)?State:{};
        StatePointer = (StatePointer)?StatePointer-1:0;
        $rootScope.$broadcast('historyStateChanged', State);
        window.onpopstate = window.onpopstate;

    }
    return {
        replaceState : function(data, title, url) {
            // replace current state
            var State = this.state();
            State = {state : data};
            window.history.replaceState(State,title,url);
            StateQueue[StatePointer] = State;
            $rootScope.$broadcast('historyStateChanged', this.state());
        },
        pushState : function(data, title, url){
            // push state and increase pointer
            var State = this.state();
            State = {state : data};
            window.history.pushState(State,title,url);
            StateQueue.push(State);
            $rootScope.$broadcast('historyStateChanged', this.state());
            StatePointer ++;
        },
        fakePush : function(data, title, url) {
            // call this when you do location.url(url)
            StateQueue.push((StateQueue.length - 1 >= 0)?StateQueue[StateQueue.length -1]:{});
            StatePointer ++;
            $rootScope.$broadcast('historyStateChanged', this.state());
        },
        state : function() {
            // get current state
            return (StateQueue[StatePointer])?StateQueue[StatePointer]:{};
        },
        popState : function(data) {
            // TODO manually popState
        },
        back : function(data) {
            // TODO needed for iphone support since iphone doesnt have a back button
        }
    }
})

在您的依赖范围上添加一些侦听器,您会像这样:

$scope.$on('historyStateChanged', function(e,v) {
        if(v)
            $scope.state = v;
        else
            $scope.state = {}
    });

我就是这样做的。在我看来,URL 只应在加载新视图时更改。我认为这就是 Angular 团队的意图。如果您想在模型上映射前进/后退按钮,请尝试使用 HTML5 的 window.history

希望我能帮上忙。干杯,海因里希

于 2013-05-24T11:16:16.597 回答
1

如果你在 2015 年登陆这里:真正的答案是不使用这些黑客(我敢这么命名,因为使用上面列出的任何方法,你将失去使用 resolve 等的可能性),而是切换到用户界面路由器

这是关于差异的方便演示。实现应该像将 $route 交换为 $state 并将状态转换为名称一样简单。

我目前正在切换到一种方法,在该方法中我将使用 a href 引用路由,并带有一个可选的 get 参数,该参数可以更改状态而不重新加载它。有关这方面的更多信息,请查看此处的“参数”部分

于 2015-02-24T22:00:43.987 回答
1

利用:

$routeProvider.when('/somewhere', {
    controller: 'SomeCtrl',
    reloadOnSearch: false
})

这将防止在查询参数更改以及哈希更改时重新加载控制器。

于 2014-11-06T20:18:18.870 回答
0

这个简单的解决方案(令人惊讶)对我有用:

$state.params.id = id;  //updating the $state.params somehow prevents reloading the state
$location.path('/articles/' + id);

它不会阻止状态重新加载后退和前进按钮。注意:我使用的是 angularjs 1.2.7 和 ui-router 0.0.1(我知道它很旧)。

于 2015-05-27T02:25:36.073 回答
0

这是插件:https ://github.com/anglibs/angular-location-update

用法:

$location.update_path('/notes/1');
于 2015-08-12T11:26:19.543 回答
0

您可以使用s promise 选项在没有$locationChange~and HistoryStatehacks的情况下执行此操作。routeresolve

假设您有一条article路线,其中该号码发生了变化,您可以这样做;

$routeProvider.when(
    '/article/:number',
    {
        templateUrl : 'partial.html',
        controller : 'ArticleCtrl',
        resolve : {
            load : ['$q', '$routeParams', function($q, $routeParams) {
                var defer = $q.defer();

                //number was specified in the previous route parameters, means we were already on the article page
                if ($routeParams.number)
                    //so dont reload the view
                    defer.reject('');
                //otherwise, the number argument was missing, we came to this location from another route, load the view
                else
                    defer.resolve();

                return defer.promise;
            }]
        }
    }
);
于 2015-12-14T04:37:25.710 回答