AngularJS UI Router - изменить URL-адрес без перезагрузки

В настоящее время наш проект использует по умолчанию $routeProvider, и я использую этот "хак", чтобы изменить url без перезагрузки страницы:

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

и в controller

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

Я думаю о замене routeProvider на ui-router для вложенных маршрутов, но не могу найти это в ui-router.

Возможно ли это - сделать то же самое с angular-ui-router?

Зачем мне это нужно? Позвольте мне объяснить на примере:
Маршрут для создания новой категории - /category/new после clicking в SAVE я показываю success-alert, и я хочу изменить маршрут /category/new на /caterogy/23 (23 - это идентификатор нового элемента, хранящегося в db)

Ответ 1

Просто вы можете использовать $state.transitionTo вместо $state.go . $state.go вызывает $state.transitionTo внутренне, но автоматически устанавливает параметры { location: true, inherit: true, relative: $state.$current, notify: true } . Вы можете вызвать $state.transitionTo и установить notify: false . Например:

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

можно заменить на

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

Изменить: Как было предложено fracz, это может быть просто:

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

Ответ 2

Хорошо, решено:) Angular UI Router имеет этот новый метод, $urlRouterProvider.deferIntercept() https://github.com/angular-ui/ui-router/issues/64

в основном это сводится к следующему:

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 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 listener *after* your custom listener
    $urlRouter.listen();
  }]);

Я думаю, что этот метод в настоящее время включен только в master версию angular ui router, тот с дополнительными параметрами (которые тоже хороши, кстати). Он должен быть клонирован и построен из источника с помощью

grunt build

Документы также доступны из источника, через

grunt ngdocs

(они встроены в каталог/site)//больше информации в README.MD

Кажется, есть другой способ сделать это с помощью динамических параметров (которые я еще не использовал). Множество кредитов для nateabele.


В качестве побочного элемента здесь необязательные параметры в angular UI Router $stateProvider, который я использовал в сочетании с выше:

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

}]);

то, что он делает, позволяет разрешить состояние, даже если отсутствует один из параметров. SEO - одна цель, читаемость другая.

В приведенном выше примере я хотел бы, чтобы doorSingle был обязательным параметром. Неясно, как их определить. Он работает нормально с несколькими необязательными параметрами, хотя на самом деле это не проблема. Обсуждение здесь https://github.com/angular-ui/ui-router/pull/1032#issuecomment-49196090

Ответ 3

Проведя много времени с этой проблемой, вот что я получил.

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

Ответ 4

Эта настройка решила следующие проблемы для меня:

  • Контроллер тренировки не вызывается дважды при обновлении URL от .../ до .../123
  • Контроллер тренировки снова не активируется при переходе в другое состояние.

Конфигурация состояния

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

Вызов состояний (из любого другого контроллера)

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

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

Контроллер обучения

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

Ответ 5

Если вам нужно только изменить URL-адрес, но предотвратить состояние изменения:

Измените местоположение с помощью (добавьте .replace, если вы хотите заменить в истории):

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

Предотвратить перенаправление в ваше состояние:

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

Ответ 6

Я сделал это, но давным-давно в версии: v0.2.10 UI-router как-то вроде этого::

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

Ответ 7

Попробуйте что-то вроде этого

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

Ответ 8

призвание

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

все равно перезагрузит контроллер.

Чтобы избежать этого, вы должны объявить параметр как динамический:

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

Тогда вам даже не нужно notify, просто позвонив

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

достаточно. Neato!

Из документации:

Если для параметра dynamic true, изменение значения параметра не приведет к входу/выходу из состояния. Решения не будут повторно получены, и представления не будут перезагружены.

[...]

Это может быть полезно для создания пользовательского интерфейса, в котором компонент обновляется при изменении значений параметров.

Ответ 9

Я не думаю, что для этого вам нужен ui-router. Документация, доступная для $location service, говорит в первом абзаце, "... изменения в $location отражаются в адресной строке браузера". Далее он продолжает говорить: "Что он не делает? Это не вызывает полную перезагрузку страницы при изменении URL-адреса браузера".

Итак, имея в виду, почему бы просто не изменить путь $location.path(поскольку этот метод является как getter, так и setter) с чем-то вроде следующего:

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

Документация отмечает, что путь всегда должен начинаться с косой черты, но это добавит его, если он отсутствует.