web-dev-qa-db-fra.com

AngularJS UI Router - changez l’URL sans recharger l’état

Actuellement, notre projet utilise le $routeProvider par défaut, et j'utilise ce "hack", pour changer url sans recharger la 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;
}]);

et dans controller

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

Je songe à remplacer routeProvider par ui-router pour les itinéraires d'emboîtement, mais je ne peux pas le trouver dans ui-router.

Est-ce possible - faire de même avec angular-ui-router?

Pourquoi ai-je besoin de cela? Laissez-moi vous expliquer avec un exemple:
La route pour créer une nouvelle catégorie est /category/new Après clicking sur SAUVEGARDER, je montre success-alert et je veux changer la route /category/new en /caterogy/23 (23 - est l’id du nouvel élément stocké dans la base de données)

121
vuliad

Vous pouvez simplement utiliser $state.transitionTo au lieu de $state.go . $state.go appelle $state.transitionTo en interne mais définit automatiquement les options sur { location: true, inherit: true, relative: $state.$current, notify: true } . Vous pouvez appeler $state.transitionTo et définir notify: false . Par exemple: 

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

peut être remplacé par

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

Edit: Comme suggéré par fracz, cela peut simplement être: 

$state.go('.detail', {id: newId}, {notify: false}) 
146
rezKesh

Ok, résolu:) Angular UI Router utilise cette nouvelle méthode, $ urlRouterProvider.deferIntercept () https://github.com/angular-ui/ui-router/issues/ 64

fondamentalement, cela revient à ceci:

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

Je pense que cette méthode est actuellement uniquement incluse dans la version master du routeur angular ui, celle avec des paramètres optionnels (qui sont aussi Nice, btw). Il doit être cloné et construit à partir de la source avec 

grunt build

Les documents sont également accessibles depuis la source, via 

grunt ngdocs

(ils sont intégrés dans le répertoire/site) // plus d'informations dans README.MD

Il semble y avoir un autre moyen de faire cela, avec des paramètres dynamiques (que je n’ai pas utilisés) . Beaucoup de crédits à nateabele.


En résumé, voici paramètres optionnels dans $ stateProvider du routeur d'interface utilisateur angulaire, que j'ai utilisés en combinaison avec ce qui précède:

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

}]);

cela permet de résoudre un état, même s'il manque l'un des paramètres. Le référencement est un objectif, la lisibilité un autre. 

Dans l'exemple ci-dessus, je souhaitais que le paramètre doorsSingle soit un paramètre obligatoire. On ne sait pas comment les définir. Cela fonctionne bien avec plusieurs paramètres facultatifs, donc pas vraiment un problème. La discussion est ici https://github.com/angular-ui/ui-router/pull/1032#issuecomment-49196090

47
wiherek

Après avoir passé beaucoup de temps avec cette question, voici ce que je travaille

$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
});
13
Pankaj Parkar

Cette configuration a résolu les problèmes suivants pour moi:

  • Le contrôleur de formation n'est pas appelé deux fois lors de la mise à jour de l'URL de .../ à .../123
  • Le contrôleur de formation ne se voit plus invoquer lors du passage à un autre état

Configuration de l'état

state('training', {
    abstract: true,
    url: '/training',
    templateUrl: 'partials/training.html',
    controller: 'TrainingController'
}).
state('training.edit', {
    url: '/:trainingId'
}).
state('training.new', {
    url: '/{trainingId}',
    // Optional Parameter
    params: {
        trainingId: null
    }
})

Invocation des états (à partir de tout autre contrôleur)

$scope.editTraining = function (training) {
    $state.go('training.edit', { trainingId: training.id });
};

$scope.newTraining = function () {
    $state.go('training.new', { });
};

Contrôleur de formation

var newTraining;

if (!!!$state.params.trainingId) {

    // new      

    newTraining = // create new training ...

    // Update the URL without reloading the controller
    $state.go('training.edit',
        {
            trainingId : newTraining.id
        },
        {
            location: 'replace', //  update url and replace
            inherit: false,
            notify: false
        });     

} else {

    // edit

    // load existing training ...
}   
7
martinoss

Si vous avez seulement besoin de changer d'URL mais d'empêcher le changement d'état:

Changer de lieu avec (ajoutez .replace si vous voulez remplacer dans l'historique):

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

Empêcher la redirection vers votre état:

$transitions.onBefore({}, function($transition$) {
 if ($transition$.$to().name === '[state name]') {
   return false;
 }
});
3
egor.xyz

je l'ai fait il y a longtemps dans la version: v0.2.10 du routeur d'interface utilisateur comme quelque chose comme ceci ::

$stateProvider
  .state(
    'home', {
      url: '/home',
      views: {
        '': {
          templateUrl: Url.resolveTemplateUrl('shared/partial/main.html'),
          controller: 'mainCtrl'
        },
      }
    })
  .state('home.login', {
    url: '/login',
    templateUrl: Url.resolveTemplateUrl('authentication/partial/login.html'),
    controller: 'authenticationCtrl'
  })
  .state('home.logout', {
    url: '/logout/:state',
    controller: 'authenticationCtrl'
  })
  .state('home.reservationChart', {
    url: '/reservations/?vw',
    views: {
      '': {
        templateUrl: Url.resolveTemplateUrl('reservationChart/partial/reservationChartContainer.html'),
        controller: 'reservationChartCtrl',
        reloadOnSearch: false
      },
      '[email protected]': {
        templateUrl: Url.resolveTemplateUrl('voucher/partial/viewVoucherContainer.html'),
        controller: 'viewVoucherCtrl',
        reloadOnSearch: false
      },
      '[email protected]': {
        templateUrl: Url.resolveTemplateUrl('voucher/partial/voucherContainer.html'),
        controller: 'voucherCtrl',
        reloadOnSearch: false
      }
    },
    reloadOnSearch: false
  })
2
sheelpriy

Essayez quelque chose comme ça

$state.go($state.$current.name, {... $state.params, 'key': newValue}, {notify: false})
0
Eyad Farra

Appel

$state.go($state.current, {myParam: newValue}, {notify: false});

rechargera toujours le contrôleur.

Pour l'éviter, vous devez déclarer le paramètre comme dynamique:

$stateProvider.state({
    name: 'myState',
    url: '/my_state?myParam',
    params: {
        myParam: {
          dynamic: true,
        }
    },
    ...
});

Ensuite, vous n'avez même pas besoin de la notify, il vous suffit d'appeler

$state.go($state.current, {myParam: newValue})

suffit. Neato!

De la documentation :

Lorsque dynamic est true, les modifications apportées à la valeur du paramètre n'entraînent pas la saisie/la sortie de l'état. Les résolutions ne seront pas récupérées et les vues ne seront pas rechargées.

[...]

Cela peut être utile pour créer une interface utilisateur dans laquelle le composant se met à jour lorsque les valeurs param changent.

0
Moritz Ringler