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)


1
в інтерфейсі маршрутизатора вам не потрібно визначати URL-адресу для кожного штату, ви можете переходити від штату до стану, не змінюючи URL-адреси
Джонатан де М.

Ви хочете оновити всю URL-адресу або лише шлях пошуку? Я шукав рішення, оновлення шляху пошуку, і знайшов його тут: stackoverflow.com/questions/21425378/…
Флоріан Лох,

@johnathan Дійсно? Я хотів би це зробити, показуючи лише одну URL-адресу, але, $urlRouterProvider.otherwiseздається, працює над URL-адресою, а не державою. Гммм, можливо, я міг би жити з двома URL-адресами або знайти інший спосіб вказати, що це недійсна URL-адреса.
Мауг каже, що повернемо Моніку

Відповіді:


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

16
Це не "зберігати URL при перезавантаженні стану" замість "змінити URL без перезавантаження стану"?
Пітер Хедберг

25
для мене це працювало з наступним: $state.transitionTo('.detail', {id: newId}, { location: true, inherit: true, relative: $state.$current, notify: false }) так, в основному, встановити сповіщення на помилкове, а розташування - на істинне
Arjen de Vries

6
@ArjendeVries Так, це працює як слід, але я виявив несподівану поведінку. Після розмови з багатьма викликами методу переходуTo (без перезавантаження), коли ви, нарешті, переходите до нового стану (тобто. Url), він знову запускає старий контролер.
Премчандра Сінгх

2
@Premchandra Singh: Маємо ту саму проблему тут. Виходячи з стану, старий контролер знову ініціалізується. Я отримую таку ж проблему з прийнятим рішенням від wiherek. Дивіться також тут github.com/angular-ui/ui-router/isissue/64
martinoss

15
Ще простіше: $state.go('.detail', {id: newId}, {notify: false}).
fracz

48

Добре, вирішено :) У кутовому маршрутизаторі інтерфейсу є цей новий метод, $ urlRouterProvider.deferIntercept () https://github.com/angular-ui/ui-router/isissue/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();
  }]);

Я думаю, що цей метод наразі включений лише у головну версію кутового маршрутизатора, що має необов’язкові параметри (які також приємні, btw). Його потрібно клонувати та будувати з джерела

grunt build

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

grunt ngdocs

(вони вбудовуються в каталог / site) // додаткова інформація в README.MD

Здається, є інший спосіб зробити це за допомогою динамічних параметрів (які я ще не використовував). Багато кредитів натеабеле.


В якості додаткового означення ось необов'язкові параметри у $ stateProvider маршрутизатора інтерфейсу UI, які я використовував у поєднанні з вищезазначеним:

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


Здається, що параметри ваших необов’язкових параметрів не працюють. Error: Both params and url specicified in state 'state'. Він каже , що в документації , що є неприпустимим використання занадто. Трохи невтішно.
Rhys van der Waerden

1
Ви будували з майстра? Зверніть увагу, що в момент, коли я додав рішення, необов'язкові параметри були включені лише у версію, яку потрібно було створити вручну з джерела. це не буде включено до випуску, поки не вийде v.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,

Я думаю, що за допомогою цього рішення браузер назад не працював би, оскільки ми змінюємо стан, не повідомляючи про це ui-роутеру
Pankaj Parkar

2
Я спробував це, і, схоже $onInit(), викликається на моєму компоненті щоразу, коли я використовую $state.go. Мені це не здається на 100% нормальним.
Карм

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

достатньо. Неато!

З документації :

Коли 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 ...
}   

Я намагався використовувати подібну стратегію, але мій контролер ніколи не отримує значення trainigId зі сторінки редагування, чогось, що мені там може бути не вистачає? Я спробував отримати доступ до сторінки редагування безпосередньо з URL-адреси та за допомогою ui-sref. Мій код точно такий, як ваш.
Нікхіл Бхандарі

Це працювало для мене, і на сьогоднішній день є найбільш зрозумілим рішенням
OoDeLally

3

Якщо вам потрібно змінити лише URL-адресу, але запобігти зміні стану:

Змініть місце розташування на (додайте .замінити, якщо ви хочете замінити в історії):

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

Запобігайте переадресації на ваш стан:

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

2

Я робив це, але давно у версії: v0.2.10 UI-роутера, як-то подібне ::

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

0

Спробуйте щось подібне

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

-6

Я не думаю, що для цього вам взагалі не потрібен ui-роутер. Документація, доступна для служби $ location, говорить у першому пункті: "... зміни в $ location відображаються в адресному рядку браузера." Пізніше він продовжує говорити: "Що це не робити? Це не викликає повного перезавантаження сторінки при зміні URL-адреси браузера".

Отже, маючи це на увазі, чому б просто не змінити $ location.path (оскільки метод є як геттером, так і сеттером) чимось на зразок наступного:

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

У документації зазначається , що шлях завжди повинен починатися з косими рисами, але це додасть його , якщо він відсутній.


Коли я використовую ui-routerта використовую $location.path(URL_PATH), сторінка автоматично відтворюється ?!
Куша

так, це відтворюється. Я спробував з event.preventDefault () на $ locationChangeStart, який також не працює - тобто він зупиняє стан повторного відображення, але також запобігає оновлення URL-адреси.
wiherek
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.