AngularJS: Change hash and route without completely reloading controller

前端 未结 10 1221
鱼传尺愫
鱼传尺愫 2020-12-07 19:29

I have a controller, with a route like this:

#/articles/1234

I want to change the route without completely reloading the controller, so I can keep the positi

相关标签:
10条回答
  • 2020-12-07 20:12

    Had the very same challange,

    Found a hack in another StackOverflow response that did the trick

    Fairly clean solution - all I did was to add these lines to the controller that sets $location.path:

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

    ..and made sure $route in injected into the controller of course.

    But still, feels like "DoNotFollowRoutesOnPathChange" is a missing feature in AngularJS.

    /Jens

    Update: Since listening to this event effectively kills further usage of $routeProvider configs, I had to limit this catch to current path only:

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

    Getting ugly...

    0 讨论(0)
  • 2020-12-07 20:12

    Here is plugin: https://github.com/anglibs/angular-location-update

    Usage:

    $location.update_path('/notes/1');
    
    0 讨论(0)
  • 2020-12-07 20:13

    Brief Answer:

    You can use the $location.search() method as you mentioned. You should listen to the "$routeUpdate" event on scope instead of other route events. $route API.

    Explanation:

    1. First of all (you already know), add reloadOnSearch: false to your $routeProvider:

      $routeProvider.when('/somewhere', {
          controller: 'SomeCtrl',
          reloadOnSearch: false
      })
      
    2. Change your anchor tag href or ng-href to href="#/somewhere?param=value" this will trigger $routeChangeSuccess event if the path part (/somewhere) is not the same as current location. Otherwise it will trigger $routeUpdate event.

    3. Listen event on scope:

      $scope.$on("$routeUpdate", function(event, route) {
          // some code here
      });
      
    4. If you want to change search params in code, you can use $location.search() method. $location.search API.

    0 讨论(0)
  • 2020-12-07 20:14

    define a factory to handle HTML5's window.history like so (note I keep an own Stack to make it work on android as well since it has a few issues there):

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

    add a few listeners on your dependent scopes and you will be fine like so:

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

    thats how I do it. In my point of view the URL should only change when a new view is loaded. I think thats how the Angular Team intended it anyways. If you want to map forward/back button on your model try to do it with HTML5's window.history

    Hope I could help. Cheers, Heinrich

    0 讨论(0)
提交回复
热议问题