Ui-router 1.x.x изменить $переход $.params() во время решения

Попытка перенести приложение angularjs для использования новой версии angular-ui-router 1.0.14 и наткнулась на проблему при попытке изменить $stateParams в разрешении состояния.

Например, ранее (при использовании angular-ui-router 0.3.2) изменение $stateParams работало следующим образом:

 $stateProvider.state('myState', {
   parent: 'baseState',
   url: '/calendar?firstAvailableDate',
   template: 'calendar.html',
   controller: 'CalendarController',
   controllerAs: 'calendarCtrl',
   resolve: {
     availableDates: ['CalendarService', '$stateParams', function(CalendarService, $stateParams) {
       return CalendarService.getAvailableDates().then(function(response){
          $stateParams.firstAvailableDate = response[0];
          return response;
       });
     }]
   }
 })

Проблема заключается в том, что firstAvailableDate заполняется после разрешения, и я не знаю, как обновить $transition$.params() во время разрешения при использовании новой версии angular-ui-router 1.0.14.

Я попытался и смог обновить параметр url с помощью

  • запуск $state.go('myState', {firstAvailableDate : response[0]}), но это перезагружает состояние, поэтому экран мерцает

  • изменен $transition$.treeChanges().to[$transition$.treeChanges().length-1].paramValues.firstAvailableDate = response[0];, чтобы фактически переопределить параметры. Я сделал это после просмотра реализации на params() для $transition$.

Несмотря на то, что оба эти параметра работают, они, похоже, являются хаками, а не книжными реализациями.

Каков правильный подход к использованию при попытке изменить параметры внутри решения?

Ответ 1

Подход с динамическим параметром:

Взгляните на этот документ: params.paramdeclaration # dynamic. Возможно, это то, что вы ищете: ...a transition still occurs....

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

Обычно, если значение параметра изменяется, состояние, в котором объявлено, что параметр будет перезагружен (введен/вышел). Когда параметр является динамическим, переход все еще происходит, но он не вызывает состояние выхода/ввода.

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

Обратите внимание, что вы не сможете включить такую ​​логику в свое решение внутри $stateProvider.state. Я бы сделал это, используя параметры dynamic, чтобы предотвратить перезагрузку состояния. К сожалению, правила dynamic не работают, когда вы пытаетесь обновить свое состояние (например, с помощью $stage.go()) внутри части решения. Поэтому я переместил эту логику в контроллер, чтобы она работала хорошо - DEMO PLNKR.

Так как userId является динамическим параметром, представление не возвращается и не возвращается снова, когда оно было изменено.

Определите свой динамический параметр:

  $stateProvider.state('userlist.detail', {
    url: '/:userId',
    controller: 'userDetail',
    controllerAs: '$ctrl',
    params: {
      userId: {
        value: '',
        dynamic: true
      }
    },
    template: `
        <h3>User {{ $ctrl.user.id }}</h3>

        <h2>{{ $ctrl.user.name }} {{ !$ctrl.user.active ? "(Deactivated)" : "" }}</h2>

        <table>
          <tr><td>Address</td><td>{{ $ctrl.user.address }}</td></tr>
          <tr><td>Phone</td><td>{{ $ctrl.user.phone }}</td></tr>
          <tr><td>Email</td><td>{{ $ctrl.user.email }}</td></tr>
          <tr><td>Company</td><td>{{ $ctrl.user.company }}</td></tr>
          <tr><td>Age</td><td>{{ $ctrl.user.age }}</td></tr>
        </table>
    `
  });

Ваш контроллер:

app.controller('userDetail', function ($transition$, $state, UserService, users) {

  let $ctrl = this;

  this.uiOnParamsChanged = (newParams) => {

    console.log(newParams);

    if (newParams.userId !== '') {
      $ctrl.user = users.find(user => user.id == newParams.userId);
    }
  };

  this.$onInit = function () {

    console.log($transition$.params());

    if ($transition$.params().userId === '') {
      UserService.list().then(function (result) {
        $state.go('userlist.detail', {userId: result[0].id});
      });
    }
  }
});

Обработать новые параметры с помощью $transition.on* крючков при запуске изменения маршрута:

Другой подход заключается в настройке правильного параметра state перед тем, как вы перейдете в свое состояние. Но вы уже сказали, это то, чего вы не хотите. Если бы я столкнулся с одной и той же проблемой: я попытался бы установить правильный параметр state перед изменением вида.

app.run(function (
    $transitions,
    $state,
    CalendarService
) {

    $transitions.onStart({}, function(transition) {
        if (transition.to().name === 'mySate' && transition.params().firstAvailableDate === '') {

            // please check this, I don't know if a "abort" is necessary
            transition.abort();

            return CalendarService.getAvailableDates().then(function(response){
                // Since firstAvailableDate is dynamic 
                // it should be handled as descript in the documents.
                return $state.target('mySate', {firstAvailableDate : response[0]});
            });
        }
    });
});

Обработать новые параметры с помощью $transition.on* крючков при запуске изменения маршрута через redirectTo

Примечание: redirectTo обрабатывается как крюк onStart, до разрешения LAZY.

Это делает то же самое, что указано выше рядом с заголовком " обрабатывать новые параметры с помощью $transition.on* крючков при запуске изменения маршрута", поскольку redirectTo также является onStart крюком с автоматическим управлением,

$stateProvider.state('myState', {
    parent: 'baseState',
    url: '/calendar?firstAvailableDate',
    template: 'calendar.html',
    controller: 'CalendarController',
    controllerAs: 'calendarCtrl',
    redirectTo: (trans) => {
        if (trans.params().firstAvailableDate === '') {
            var CalendarService = trans.injector().get('CalendarService');
            return CalendarService.getAvailableDates().then(function(response){
                return { state: 'myState', params: { firstAvailableDate: response[0] }};
            });
        }
    }
});