AngularJS UI Router - change url without reloading

2019-01-03 20:17发布

Currently our project is using default $routeProvider, and I am using this "hack", to change url without reloading page:

services.service('$locationEx', ['$location', '$route', '$rootScope', function($location, $route, $rootScope) {
    $location.skipReload = function () {
        var lastRoute = $route.current;
        var un = $rootScope.$on('$locationChangeSuccess', function () {
            $route.current = lastRoute;
            un();
        });
        return $location;
    };
    return $location;
}]);

and in controller

$locationEx.skipReload().path("/category/" + $scope.model.id).replace();

I am thinking of replacing routeProvider with ui-router for nesting routes, but cant find this in ui-router.

Is it possible - do the same with angular-ui-router?

Why do I need this? Let me explain with an example :
Route for creating new category is /category/new after clicking on SAVE I show success-alert and I want to change route /category/new to /caterogy/23 (23 - is id of new item stored in db)

8条回答
三岁会撩人
2楼-- · 2019-01-03 20:56

Simply you can use $state.transitionTo instead of $state.go . $state.go calls $state.transitionTo internally but automatically sets options to { location: true, inherit: true, relative: $state.$current, notify: true } . You can call $state.transitionTo and set notify: false . For example:

$state.go('.detail', {id: newId}) 

can be replaced by

$state.transitionTo('.detail', {id: newId}, {
    location: true,
    inherit: true,
    relative: $state.$current,
    notify: false
})

Edit: As suggested by fracz it can simply be:

$state.go('.detail', {id: newId}, {notify: false}) 
查看更多
该账号已被封号
3楼-- · 2019-01-03 20:56

I don't think you need ui-router at all for this. The documentation available for the $location service says in the first paragraph, "...changes to $location are reflected into the browser address bar." It continues on later to say, "What does it not do? It does not cause a full page reload when the browser URL is changed."

So, with that in mind, why not simply change the $location.path (as the method is both a getter and setter) with something like the following:

var newPath = IdFromService;
$location.path(newPath);

The documentation notes that the path should always begin with a forward slash, but this will add it if it's missing.

查看更多
Root(大扎)
4楼-- · 2019-01-03 20:59

Ok, solved :) Angular UI Router has this new method, $urlRouterProvider.deferIntercept() https://github.com/angular-ui/ui-router/issues/64

basically it comes down to this:

angular.module('myApp', [ui.router])
  .config(['$urlRouterProvider', function ($urlRouterProvider) {
    $urlRouterProvider.deferIntercept();
  }])
  // then define the interception
  .run(['$rootScope', '$urlRouter', '$location', '$state', function ($rootScope, $urlRouter, $location, $state) {
    $rootScope.$on('$locationChangeSuccess', function(e, newUrl, oldUrl) {
      // Prevent $urlRouter's default handler from firing
      e.preventDefault();

      /** 
       * provide conditions on when to 
       * sync change in $location.path() with state reload.
       * I use $location and $state as examples, but
       * You can do any logic
       * before syncing OR stop syncing all together.
       */

      if ($state.current.name !== 'main.exampleState' || newUrl === 'http://some.url' || oldUrl !=='https://another.url') {
        // your stuff
        $urlRouter.sync();
      } else {
        // don't sync
      }
    });
    // Configures $urlRouter's listener *after* your custom listener
    $urlRouter.listen();
  }]);

I think this method is currently only included in the master version of angular ui router, the one with optional parameters (which are nice too, btw). It needs to be cloned and built from source with

grunt build

The docs are accessible from the source as well, through

grunt ngdocs

(they get built into the /site directory) // more info in README.MD

There seems to be another way to do this, by dynamic parameters (which I haven't used). Many credits to nateabele.


As a sidenote, here are optional parameters in Angular UI Router's $stateProvider, which I used in combination with the above:

angular.module('myApp').config(['$stateProvider', function ($stateProvider) {    

  $stateProvider
    .state('main.doorsList', {
      url: 'doors',
      controller: DoorsListCtrl,
      resolve: DoorsListCtrl.resolve,
      templateUrl: '/modules/doors/doors-list.html'
    })
    .state('main.doorsSingle', {
      url: 'doors/:doorsSingle/:doorsDetail',
      params: {
        // as of today, it was unclear how to define a required parameter (more below)
        doorsSingle: {value: null},
        doorsDetail: {value: null}
      },
      controller: DoorsSingleCtrl,
      resolve: DoorsSingleCtrl.resolve,
      templateUrl: '/modules/doors/doors-single.html'
    });

}]);

what that does is it allows to resolve a state, even if one of the params is missing. SEO is one purpose, readability another.

In the example above, I wanted doorsSingle to be a required parameter. It is not clear how to define those. It works ok with multiple optional parameters though, so not really a problem. The discussion is here https://github.com/angular-ui/ui-router/pull/1032#issuecomment-49196090

查看更多
倾城 Initia
5楼-- · 2019-01-03 21:05

After spending a lot of time with this issue, Here is what I got working

$state.go('stateName',params,{
    // prevent the events onStart and onSuccess from firing
    notify:false,
    // prevent reload of the current state
    reload:false, 
    // replace the last record when changing the params so you don't hit the back button and get old params
    location:'replace', 
    // inherit the current params on the url
    inherit:true
});
查看更多
该账号已被封号
6楼-- · 2019-01-03 21:09

If you need only change url but prevent change state:

Change location with (add .replace if you want to replace in history):

this.$location.path([Your path]).replace();

Prevent redirect to your state:

$transitions.onBefore({}, function($transition$) {
 if ($transition$.$to().name === '[state name]') {
   return false;
 }
});
查看更多
不美不萌又怎样
7楼-- · 2019-01-03 21:09

Try something like this

$state.go($state.$current.name, {... $state.params, 'key': newValue}, {notify: false})
查看更多
登录 后发表回答