Как сохранить необязательный параметр состояния в браузере обратно в ui-router?

У меня есть одно родительское состояние, внутри которого есть два дочерних состояния, и я собираюсь показать одно состояние на основе URL.

Из этих двух states один имеет такие параметры, как param1 и param2, я использую params опцию ui-router внутри определения состояния.

Штат

$stateProvider.state('tabs.account', {
    url: '/account',
    views: {
        'content@tabs': {
            templateUrl: 'account.html',
            controller: function($scope, $stateParams) {
                //This params are internally used to make ajax and show some data.
                $scope.param1 = $stateParams.param1;
                $scope.param2 = $stateParams.param2;
            },
        }
    },
    params: {
        param1: { value: null }, //this are optional param
        param2: { value: null } //because they are not used in url
    }
});

Если вы посмотрите на мой маршрут, параметр params на самом деле не представлен внутри URL, поэтому я рассматриваю его как необязательный.

Проблема

Посмотрите на plunkr, я показал две вкладки Аккаунт и Опрос,

  1. Нажмите на вкладку «Обзор», затем добавьте некоторые данные в textarea, которые показаны.
  2. Нажмите Перейти к учетной записи, чтобы передать эти значения textarea на другую вкладку Учетная запись, выполнив ui-sref="tabs.account({param1: thing1, param2: thing2})" на привязке.

  3. Теперь вы увидите значения param1 и param2 в html, которые были назначены области видимости из $stateParams.

  4. Теперь снова нажмите на вкладку Опрос, и вы попадете на страницу опроса.
  5. Просто щелкните браузер назад, вы заметите, что значение param не становится null.

Проблема Plunkr

Я полагаю, вы получили то, что я хотел спросить, почему значение необязательного параметра не было сохранено? поскольку они были частью государства.

Я знаю, что могу решить эту проблему с помощью двух решений.

  • Создав один сервис, который будет обмениваться данными между двумя представлениями.
  • Добавляя параметр внутри URL-адреса состояния. как url: '/account/:param1/:param2', (но я бы не предпочел это)

Я уже пробовал angular-ui-routers sticky states но мне кажется это не подходит. Каков лучший способ для этого?

Есть ли способ, с помощью которого я могу заставить работать мой вариант использования, буду признателен за любые идеи.

Github Ссылка на выпуск здесь


person Pankaj Parkar    schedule 21.07.2015    source источник
comment
Только что нашел это github.com/angular-ui/ui-router/issues/1158 об атрибуте уведомления, который может быть установлен в значение false для предотвращения событий (таких как перезагрузка контроллера), но на самом деле он не работает (ошибка).   -  person Okazari    schedule 23.07.2015
comment
@Okazari, спасибо за комментарий .. Мне тоже нужно будет следить за этой проблемой :(   -  person Pankaj Parkar    schedule 23.07.2015
comment
Рассматривали ли вы возможность использования параметров запроса вместо параметров URL? Это может работать и будет немного менее странным в URL-адресе ('/url/route?param1=value¶m2=value')   -  person Okazari    schedule 24.07.2015
comment
@Okazari Спасибо за предложение, но я не хотел этого делать. В основном это то же самое, что я делаю здесь.   -  person Pankaj Parkar    schedule 24.07.2015
comment
@downvoter, пожалуйста, комментируйте, пока вы голосуете против. Есть ли какие-либо проблемы с вопросом?   -  person Pankaj Parkar    schedule 04.09.2015


Ответы (4)


Я бы переместил определение params в родительское состояние, чтобы разделить необязательные параметры состояния между двумя вашими дочерними состояниями.

Дочерние состояния унаследуют $stateParams от вашего родителя, поэтому нет необходимости в реальном «обходном пути».

Просто добавьте $stateParams, как обычно, в ваши дочерние контроллеры, и вы получите полный доступ к передаваемым параметрам. Если вы не хотите использовать параметры в определенном дочернем состоянии, просто избегайте их внедрения.

Это работает с;

  • Кнопка назад
  • Кнопка «Вперед»
  • ui-sref (без параметров (оставится как есть))
  • ui-sref (с параметрами (будет перезаписан))

$stateProvider
  .state('parent', {
    params: { p1: null, p2: null }
  })
  .state('parent.childOne', {
    url: '/one',
    controller: function ($stateParams) {
      console.log($stateParams); // { p1: null, p2: null }
    }
  })
  .state('parent.childTwo', {
    url: '/two',
    controller: function ($stateParams) {
      console.log($stateParams); // { p1: null, p2: null }
    }
  })

Если вы в какой-то момент захотите очистить параметры во время путешествия по дереву состояний parent, вам придется сделать это вручную.

Это было бы единственным реальным предостережением, которое я вижу, используя это решение.

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

обновлен плункер

person Kasper Lewau    schedule 24.07.2015
comment
Это тоже хорошее решение. Просто обратите внимание, что может потребоваться дополнительное управление параметрами, если вы хотите, чтобы параметры сохранялись при навигации из других состояний (представьте, что вы идете куда-то совершенно по-другому в приложении или извне, а затем используете кнопку «Назад» в браузере — не уверен, что это один из ваших сценарии использования). - person Christian Yang; 26.07.2015
comment
Параметры не сохраняются, если вы переходите в состояние, не имеющее представления о параметрах на месте, то же самое касается кнопки «Назад». Если состояние знает о поступающих параметрах, все в порядке. Таким образом, вы можете продолжать перетасовывать определение одного параметра выше в иерархии состояний, но когда это начинает становиться глупым, я бы переместил его в службу. - person Kasper Lewau; 26.07.2015
comment
Вариант использования, который я рассматриваю для совместного использования параметров, — это несколько состояний, которые имеют логическую связь с передаваемыми параметрами. Я бы ни в коем случае не рекомендовал его использовать, если идея состоит в том, чтобы передавать данные и сохранять их на протяжении большей части/всего приложения. - person Kasper Lewau; 26.07.2015
comment
@KasperLewau есть предложения, когда я должен очистить эти значения ... что вы думаете об этом? - person Pankaj Parkar; 29.07.2015
comment
@PankajParkar Ну, поскольку вы делитесь значениями между всеми дочерними состояниями родителя, который содержит атрибут params, он автоматически очищается после перехода из этого дерева состояний, а затем снова в без передача параметров при входе. Это действительно зависит от вашего варианта использования, когда вам нужно очистить его? - person Kasper Lewau; 30.07.2015
comment
@KasperLewau Мне бы понравился этот подход, который подходит для моего варианта использования - person Pankaj Parkar; 30.07.2015

Одним из обходных решений является кэширование параметров состояния и их условная загрузка при переходе в состояние tabs.account. Конфигурация состояния маршрутизатора пользовательского интерфейса фактически позволяет вам предоставить обратный вызов onEnter для таких типов ситуаций «сделать что-нибудь при входе в состояние».

Вот основная логика использования localStorage в качестве кеша с рабочий плункер здесь:

  • When you enter the tabs.account state, check for your state params
    • If you have them, cache them to local storage
    • Если нет, загрузите их из локального хранилища в $stateParams.

Вот пример фрагмента кода для справки (взято из Plunker):

  $stateProvider.state('tabs.account', {
    ...
    onEnter: ['$stateParams', '$window', function($stateParams, $window) {
      if($stateParams.param1) {
        $window.localStorage.setItem('tabs.account.param1', $stateParams.param1);
      } else {
        $stateParams.param1 = $window.localStorage.getItem('tabs.account.param1');
      }
      if($stateParams.param2) {
        $window.localStorage.setItem('tabs.account.param2', $stateParams.param2);
      } else {
        $stateParams.param2 = $window.localStorage.getItem('tabs.account.param2');
      }
    }],
    ...
  }

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

И последнее замечание: в Plunker я напрямую обращаюсь к локальному хранилищу (через службу Angular $window). Возможно, вы захотите использовать какой-нибудь модуль AngularJS — я использовал angular-local-storage в производство.

person Christian Yang    schedule 24.07.2015
comment
Если вы не хотите сохраняться бесконечно, просто используйте SessionStorage вместо LocalStorage. SessionStorage имеет время жизни сеанса приложения (обновить/закрыть, очистить хранилище сеансов) - person Okazari; 24.07.2015
comment
@Okazari, будет ли sessionStorage/localStorage работать в старом браузере, таком как IE9/IE10? - person Pankaj Parkar; 25.07.2015
comment
@PankajParkar IE9/10 полностью поддерживает обе эти технологии. можете использовать? - person Kasper Lewau; 25.07.2015
comment
@Okazari, тогда это выглядит многообещающе .. вернемся к этому ответу .. после некоторого тестирования - person Pankaj Parkar; 25.07.2015
comment
@PankajParkar Не пытаюсь переманить на принятие, но вы взглянули на мой предложенный ответ? Пожалуй, самый чистый из всех. - person Kasper Lewau; 25.07.2015
comment
@KasperLewau да, я смотрю на ... в настоящее время у меня нет моего кода здесь ... Я обязательно посмотрю на него в понедельник утром ... Я уже проголосовал за твой ответ, чувак ... :) - person Pankaj Parkar; 25.07.2015
comment
@PankajParkar Тогда хорошо! Извините за назойливость. идем дальше о, и это абсолютно не означает, что решение local/sessionStorage плохое — вовсе нет. - person Kasper Lewau; 25.07.2015
comment
@KasperLewau Полностью. Как вы сказали, несмотря на то, что у вас выглядит чище, использование локального/сеансового хранилища не так уж и странно. Мы использовали его в моем первом проекте angular (это был выбор, сделанный экспертами angular), чтобы сохранить некоторую навигационную информацию (например, положение прокрутки в бесконечной прокрутке, поисковые фильтры и т. д.). - person Okazari; 25.07.2015
comment
@ChristianYang Я проверил ваш plunkr .. как мне очистить значения .. каждый раз, когда они сохраняются. Я думаю, что это похоже на подход обслуживания/завода при его перестановке на localStorage/sessionStorage - person Pankaj Parkar; 29.07.2015
comment
Привет @PankjParkar. Знаете ли вы, при каких обстоятельствах вы хотели бы очистить значения? Это обновление страницы, похожее на веб-форму? Если это так, вы можете позвонить localStorage.clear() (или sessionStorage.clear()) в app.run. Вот обновленный plunkr с очисткой в ​​app.run и использованием sessionStorage: plnkr.co/edit/1LFTxN?p= предварительный просмотр. Если вы сообщите мне о других сценариях, в которых вы хотели бы очистить значения, я был бы рад пройти через них. - person Christian Yang; 30.07.2015
comment
И, честно говоря, вы правы в том, что этот обходной путь в некотором роде похож на использование хранилища в качестве эквивалента общей службы. Вероятно, наиболее полезной частью в вашем случае является использование обратного вызова состояния onEnter для извлечения из общей службы/хранилища/другого кеша, чтобы к тому времени, когда вы доберетесь до своего контроллера состояния, вы могли просто использовать функцию $stateParams. По крайней мере, таким образом проблема сохранения необязательных параметров абстрагируется от кода контроллера. - person Christian Yang; 30.07.2015

Я считаю, что то, чего вы хотите достичь, невозможно без использования одного из двух предоставленных вами решений.

Кнопка «Назад» в браузере просто сохраняет историю URL. Он понятия не имеет о внутренних состояниях ui-router и просто заставит URL-адрес измениться.

Принудительное изменение URL-адреса вызовет внутреннюю машину ui-router, но, к сожалению, ui-router увидит изменение URL-адреса так же, как если бы кто-то изменил URL-адрес вручную.

Ui-router запустит новое изменение маршрута для маршрута, указанного URL-адресом. Это означает, что он не знает, что вы хотели вернуться «назад», и просто изменит состояние на новое без каких-либо параметров.

Сводка

Нажатие кнопки «Назад» приведет к изменению состояния на новое состояние в соответствии с URL-адресом вместо возврата к предыдущему состоянию.

Вот почему добавление параметров к URL-адресу решает проблему. Поскольку URL-адрес является дискриминационным, вы, наконец, попадете в желаемое состояние.

Надеюсь, это помогло.

person Okazari    schedule 21.07.2015
comment
Я понимаю, что могу решить это только с помощью этих двух решений ... из которых добавление параметра не является предпочтительным, я думаю, это путается, когда у меня есть несколько параметров. - person Pankaj Parkar; 21.07.2015
comment
@PankajParkar Тогда ваше сервисное решение может быть тем самым. - person Okazari; 21.07.2015
comment
да..но я ищу что-то, что отличается от того, что я думаю..должен быть какой-то способ..что я упускаю.. - person Pankaj Parkar; 21.07.2015

Для меня это звучит как X Y проблема. Предлагаются способы сделать параметры состояния постоянными, тогда как проблема находится вне этой поверхности.

По определению вопроса есть данные, которые должны храниться независимо от состояний. Так что это что-то глобальное по отношению к состояниям. Таким образом, должна быть служба, которая хранит его, а контроллеры обоих состояний поддерживают его по мере необходимости. Просто не передавайте данные, выходящие за рамки одного состояния, в параметрах состояния.

Липкие состояния легко подходят для этого подхода, поскольку позволяют сохранять DOM и $scope, пока активно другое состояние. Но это не имеет ничего общего с параметрами состояния, когда состояние повторно активируется.

person Kirill Slatin    schedule 24.07.2015
comment
@krill Не могли бы вы привести мне пример с липкими состояниями .. Я пробовал, но у меня это не сработало .. Спасибо :) - person Pankaj Parkar; 28.07.2015
comment
@PankajParkar Конечно, вещь. В этом случае вам даже не нужны липкие состояния из ui-router-extras. Дайте мне знать, если вам нужна помощь с более сложным сценарием (который, я уверен, у вас есть для реальной цели) - person Kirill Slatin; 28.07.2015
comment
@krill Созданный вами plunkr следовал одному из подходов, который является сервисным подходом, который я предложил в самом своем вопросе. - person Pankaj Parkar; 28.07.2015
comment
@PankajParkar это действительно оптимальный подход к этой задаче, и я объяснил, почему. Прикрепленное решение будет выглядеть следующим образом: этим. Ваша проблема заключалась в том, что липкие состояния должны занимать различное имя пользовательского интерфейса. каждый. Однако этот подход не передает параметры в account при нажатии в меню. - person Kirill Slatin; 28.07.2015