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

134

В настоящее время наш проект использует значение по умолчанию $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)

vuliad
источник
1
в ui-router вам не нужно определять URL-адрес для каждого состояния, вы можете перемещаться из состояния в состояние, не меняя URL-адрес
Джонатан де М.
вы хотите обновить весь URL-адрес или только путь поиска? Я искал решение, обновляющее путь поиска, и нашел его здесь: stackoverflow.com/questions/21425378/…
Флориан Лох
@johnathan Правда? Я бы хотел сделать это, показывая только один URL-адрес, но, $urlRouterProvider.otherwiseпохоже, работает с URL-адресом, а не с состоянием. Хммм, может быть, я смогу жить с двумя URL-адресами или найти другой способ указать, что это недопустимый URL-адрес.
Mawg требует восстановить Монику

Ответы:

164

Просто можешь использовать $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}) 
rezKesh
источник
16
Разве это не «сохранить URL-адрес при перезагрузке состояния» вместо «изменить URL-адрес без перезагрузки состояния»?
Питер Хедберг
25
для меня это сработало со следующим: $state.transitionTo('.detail', {id: newId}, { location: true, inherit: true, relative: $state.$current, notify: false }) поэтому в основном установите для notify значение false, а для местоположения - значение true
Арьен де Врис,
6
@ArjendeVries Да, он работает, как ожидалось, но я обнаружил неожиданное поведение. После того, как вы поигрались с вызовами многих методов transitionTo (без перезагрузки), когда вы, наконец, перейдете в новое состояние (например, url), он повторно инициирует старый контроллер.
Премчандра Сингх
2
@Premchandra Singh: Здесь та же проблема. При выходе из состояния старый контроллер снова инициализируется. У меня такая же проблема с принятым решением от wiherek. См. Также здесь github.com/angular-ui/ui-router/issues/64
martinoss
15
Еще проще: $state.go('.detail', {id: newId}, {notify: false}).
fracz
48

Хорошо, решено :) В 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'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();
  }]);

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

grunt build

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

grunt ngdocs

(они встроены в каталог / site) // подробнее в README.MD

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


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

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

wiherek
источник
Кажется, что ваши дополнительные параметры не работают. Error: Both params and url specicified in state 'state', Он говорит , что в документации , что является недопустимым использование слишком. Немного разочаровывает.
Рис ван дер Варден
1
Вы строили из мастера? Обратите внимание, что на момент добавления решения дополнительные параметры были включены только в версию, которую нужно было собрать вручную из исходного кода. это не будет включено в выпуск, пока не будет выпущена версия 0.3.
wiherek
1
Просто быстрая заметка. Благодаря nateabele дополнительные параметры доступны в v0.2.11, которая была выпущена всего несколько дней назад.
rich97
это очень сбивает с толку: как использовать $ urlRouterProvider.deferIntercept (); чтобы я мог обновить параметры без перезагрузки контроллера? На самом деле это мне не показывает. В функции запуска вы можете оценить оператор if для синхронизации или отсутствия синхронизации, но все, с чем мне нужно работать, - это старый и новый URL-адреса. Как я узнаю, что это тот случай, когда все, что я хочу сделать, это обновить параметры, указав только два URL-адреса для работы? Будет ли логика ... (если старое состояние и новое состояние одинаковы, тогда не перезагружайте контроллер?) Я запутался.
btm1
правильно, я думаю, что этот вариант использования был с вложенными состояниями, я не хотел перезагружать дочернее состояние, поэтому перехватывал. Я думаю, что сейчас я бы предпочел сделать это с абсолютным таргетингом взглядов и определением взгляда на состояние, которое, как я знаю, не изменится. Во всяком случае, это все еще хорошо. Вы получаете полные URL-адреса, то есть вы можете угадать состояние по URL-адресу. А также параметры запроса и пути и т. Д. Если вы создаете свое приложение на основе состояний и URL-адресов, это большая часть информации. В блоке выполнения вы также можете получить доступ к службам и т. Д. Это ответ на ваш вопрос?
wiherek
16

Потратив много времени на эту проблему, вот что у меня получилось

$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
});
Панкадж Паркар
источник
Остальные решения связаны с провайдером маршрута. Это решение работает для случая, подобного моему, когда я использую $ stateProvider, а не $ routeProvider.
eeejay
@eeejay в основном вопрос был задан ui-routerтолько для того, как вы можете сказать, что другие решения работали $routerProvider, $routeProviderи $stateProviderсовершенно другие по архитектуре ..
Pankaj Parkar
что, если мы не хотим, чтобы с этим работала кнопка «Назад»? Я имею в виду, при обратном нажатии перейти к предыдущему состоянию / URL-адресу, а не к тому же состоянию с разными параметрами
gaurav5430
Я предполагаю, что с помощью этого решения браузер не будет работать, так как мы меняем состояние, не
сообщая
2
Я пробовал это, и похоже, что $onInit()мой компонент вызывается каждый раз, когда я использую $state.go. Мне это не кажется на 100% нормальным.
Carrm
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 , изменение значения параметра не приведет к входу / выходу из состояния. Решения не будут повторно загружаться, и просмотры не будут перезагружены.

[...]

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

Мориц Ринглер
источник
7

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

  • Контроллер обучения не вызывается дважды при обновлении 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 ...
}   
martinoss
источник
Я пытался использовать аналогичную стратегию, но мой контроллер так и не получил значение trainigId со страницы редактирования. Что-нибудь, чего мне там не хватает? Я попытался получить доступ к странице редактирования непосредственно по URL-адресу и с помощью ui-sref. Мой код в точности похож на ваш.
Нихил Бхандари
Это сработало для меня, и это, безусловно, самое ясное решение
OoDeLally
3

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

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

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

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

$transitions.onBefore({}, function($transition$) {
 if ($transition$.$to().name === '[state name]') {
   return false;
 }
});
egor.xyz
источник
2

Я сделал это, но давно, в версии: 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
      },
      '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
  })
Sheelpriy
источник
0

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

$state.go($state.$current.name, {... $state.params, 'key': newValue}, {notify: false})
Эйад Фарра
источник
-6

Думаю, для этого вообще не нужен ui-router. В документации, доступной для службы $ location, в первом абзаце сказано: «... изменения $ location отражаются в адресной строке браузера». Позже он продолжает говорить: «Чего он не делает? Он не вызывает полной перезагрузки страницы при изменении URL-адреса браузера».

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

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

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

Xaniff
источник
Когда я использую ui-routerи использую $location.path(URL_PATH), страница автоматически перерисовывается ?!
Kousha 08
да, он перерисовывает. Я пробовал использовать event.preventDefault () в $ locationChangeStart, но это тоже не работает - то есть останавливает повторную отрисовку состояния, но также предотвращает обновление URL-адреса.
wiherek