AngularJS UI Router – mude o URL sem recarregar o estado

Atualmente, nosso projeto está usando o padrão $routeProvider e estou usando esse “hack” para alterar o url sem recarregar a página:

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

e no controller

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

Estou pensando em replace o routeProvider pelo ui-router por rotas de aninhamento, mas não consigo encontrar isso no ui-router .

É possível – faça o mesmo com o angular-ui-router ?

Por que eu preciso disso? Deixe-me explicar com um exemplo :
Rota para criar nova categoria é /category/new depois de clicking em Salvar eu mostrar success-alert e eu quero mudar de rota /category/new para /caterogy/23 (23 – é id de novo item armazenado em db)

Simplesmente você pode usar $state.transitionTo vez de $state.go . $state.go chama $state.transitionTo internamente, mas define automaticamente as opções para { location: true, inherit: true, relative: $state.$current, notify: true } . Você pode chamar $state.transitionTo e set notify: false . Por exemplo:

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

pode ser substituído por

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

Edit: Como sugerido por fracz pode ser simplesmente:

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

Ok, resolvido 🙂 O Angular UI Router tem esse novo método, $ urlRouterProvider.deferIntercept () https://github.com/angular-ui/ui-router/issues/64

basicamente tudo se resume a isso:

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

Eu acho que este método está atualmente incluído apenas na versão master do roteador angular, o único com parâmetros opcionais (que são bons também, btw). Ele precisa ser clonado e construído a partir da fonte com

 grunt build 

Os documentos também podem ser acessados ​​da fonte, por meio de

 grunt ngdocs 

(eles são construídos no diretório / site) // mais informações em README.MD

Parece haver outra maneira de fazer isso, por parâmetros dynamics (que eu não usei). Muitos créditos para nateabele.


Como um sidenote, aqui estão os parâmetros opcionais no $ stateProvider do Angular UI Router, que usei em combinação com o acima:

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

o que isso faz é permitir resolver um estado, mesmo se um dos parâmetros estiver faltando. SEO é um propósito, legibilidade outro.

No exemplo acima, eu queria que portasSingle fosse um parâmetro obrigatório. Não está claro como defini-las. Funciona bem com múltiplos parâmetros opcionais, portanto não é um problema. A discussão está aqui https://github.com/angular-ui/ui-router/pull/1032#issuecomment-49196090

Depois de passar muito tempo com esse problema, aqui está o que eu comecei a trabalhar

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

Esta configuração resolveu os seguintes problemas para mim:

  • O controlador de treinamento não é chamado duas vezes ao atualizar o URL de .../ to .../123
  • O controlador de treinamento não está sendo chamado novamente ao navegar para outro estado

Configuração de estado

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

Invocando os estados (de qualquer outro controlador)

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

Controlador de Treinamento

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

Eu fiz isso, mas há muito tempo na versão: v0.2.10 do roteador de interface do usuário como algo como isto:

 $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 }, 'viewVoucher@home.reservationChart': { templateUrl: Url.resolveTemplateUrl('voucher/partial/viewVoucherContainer.html'), controller: 'viewVoucherCtrl', reloadOnSearch: false }, 'addEditVoucher@home.reservationChart': { templateUrl: Url.resolveTemplateUrl('voucher/partial/voucherContainer.html'), controller: 'voucherCtrl', reloadOnSearch: false } }, reloadOnSearch: false }) 

Se você precisar alterar apenas o URL, mas impedir o estado de alteração:

Altere o local com (add .replace, se quiser replace no histórico):

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

Impedir o redirecionamento para o seu estado:

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

Tente algo assim

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

Eu não acho que você precise de roteador ui para isso. A documentação disponível para o serviço $ location diz no primeiro parágrafo, “… as alterações no $ location são refletidas na barra de endereços do navegador”. Mais tarde, continua a dizer: “O que isso não faz? Ele não causa um recarregamento total da página quando a URL do navegador é alterada”.

Então, com isso em mente, por que não simplesmente mudar o $ location.path (como o método é tanto um getter quanto um setter) com algo como o seguinte:

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

A documentação observa que o caminho deve sempre começar com uma barra, mas isso o adicionará se estiver faltando.