Потеря данных компонента Angular View во время обновления при перезагрузке с использованием BehaviorSubject

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

Затем, на 4-й странице, я показываю сводку деталей, собранных на 3-х страницах, в виде текста, доступного только для чтения.

Я реализовал буферизацию данных, используя следующую статью:

https://www.infragistics.com/community/blogs/b/infragistics/posts/simplest-way-to-share-data-between-two-unrelated-components-in-angular

Что использует BehaviorSubject

Ниже приведена моя логика службы данных:

    import { Injectable } from '@angular/core';
    import { HttpClient, HttpHeaders } from '@angular/common/http';
    import { Observable, BehaviorSubject, throwError } from 'rxjs';
    import { retry, catchError } from 'rxjs/operators';
    import { UserResponse } from  './user-response';

    @Injectable({
      providedIn: 'root'
    })

    export class UserService {

        //Details Page 1
        public salutation: BehaviorSubject<string>;
        public firstName: BehaviorSubject<string>;

      // Base url
      baseurl = 'https://localhost:8080/rest';

      constructor(private http: HttpClient) { }

      // Http Headers
      httpOptions = {
        headers: new HttpHeaders({
          'Content-Type': 'application/json'
        })
      }

      // POST
      createUser(data): Observable<UserResponse> {
        alert('user - details = '+ data);
        return this.http.post<UserResponse>(this.baseurl + '/user', JSON.stringify(data), this.httpOptions)
        .pipe(
          retry(1),
          catchError(this.errorHandler)
        )
      }

      // Error handling
      errorHandler(error) {
         let errorMessage = '';
         if(error.error instanceof ErrorEvent) {
           // Get client-side error
           errorMessage = error.error.message;
         } else {
           // Get server-side error
           errorMessage = `Error Code: ${error.status}\nMessage: ${error.message}`;
         }
         console.log(errorMessage);
         return throwError(errorMessage);
      }

}

Ниже мой первый компонент представления:

import { Component, OnInit } from '@angular/core';
import { NgForm } from '@angular/forms';
import { UserService } from '../service/user.service';
import { BehaviorSubject } from 'rxjs';

@Component({
  selector: 'app-details1',
  templateUrl: './details1.component.html',
  styleUrls: ['./details1.component.css']
})
export class Details1Component implements OnInit {
  salutation: string;
  firstName: string;

  constructor(private userService: UserService) { }

  ngOnInit(): void {
  }

  goToDetails2(form : NgForm) {
          this.userService.salutation = new BehaviorSubject(form.value.salutation);
          this.userService.firstName = new BehaviorSubject(form.value.firstName);
      }

}

Ниже приведен фрагмент моего окончательного / сводного компонента представления.

import { Component, OnInit} from '@angular/core';
import { Router } from '@angular/router';
import { UserService } from '../service/user.service';

@Component({
  selector: 'app-summary',
  templateUrl: './summary.component.html',
  styleUrls: ['./summary.component.css']
})

export class SummaryComponent implements OnInit {

       salutation: string;
       firstName: string;

  constructor(
    private router: Router,
    public userService: UserService
  ) {
   }

   ngOnInit(): void {
       this.userService.salutation.subscribe(c => { this.salutation = c; });
       this.userService.firstName.subscribe(c => { this.firstName = c; });
   }

}

На последней странице мои выдержки из html выглядят следующим образом:

<form #detailsForm4="ngForm">
<div class="govuk-form-group">
  <table class="govuk-table">
    <thead class="govuk-table__head">
    <tr class="govuk-table__row"></tr>
    </thead>
    <tbody class="govuk-table__body" align="left">
    <tr class="govuk-table__row">
      <th scope="row" class="govuk-table__header">Salutation</th>
      <td class="govuk-table__cell">   {{salutation}} </td>
      <td class="govuk-table__cell"> </td>
    </tr>
    <tr class="govuk-table__row">
      <th scope="row" class="govuk-table__header">First name</th>
      <td class="govuk-table__cell">   {{firstName}} </td>
      <td class="govuk-table__cell"> </td>
    </tr>

Данные отображаются на сводной странице, как они записаны в формах на страницах 1-3, НО данные теряются при обновлении страницы или когда моя машина переходит в спящий режим и т. Д.

Я читал о сохранении локального и сеансового хранилища и использовал его с дней angularjs 1.x. Много лет назад я был действительно удивлен, когда данные исчезли на моем итоговом экране!

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

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

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

Кроме того, подход, который вписывается в Angular9 BehaviorSubject - стек rxjs ИЛИ какие-либо недавние функции, потому что я знаю, что Angular развился со времен AngularJs 1.x?

Сводная информация изменяется только тогда, когда пользователь возвращается на любую из страниц 1-3, чтобы изменить детали формы.

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

Большое спасибо.


person Mega    schedule 24.03.2020    source источник
comment
Кажется очевидным, что при обновлении данные теряются. Одно из решений - хранить данные в локальном хранилище или, если это не вводимые пользователем данные, получить их снова с сервера после обновления.   -  person Stefan    schedule 25.03.2020
comment
Спасибо, Стефан, за быстрый ответ. Я не смог найти хороший фрагмент об использовании локального хранилища, который подходит для angular9. Требуется подтверждение передового опыта и т. Д.   -  person Mega    schedule 25.03.2020
comment
Скорее всего, это проблема, связанная с неожиданной перезагрузкой UserService. Чтобы доказать, что в логе ngOnInit сообщение на консоль. Затем воссоздайте проблему. Если вы видите более одного сообщения ... вы можете подумать о том, чтобы сделать объекты поведения статическими, что потребует немного больше усилий для обеспечения того, чтобы контент всегда был правильным.   -  person JWP    schedule 25.03.2020


Ответы (1)


Я предлагаю вам использовать localStorage в вашем SummaryComponent в хуке жизненного цикла ngOnDestroy. Он всегда будет устанавливать ваши данные в localStorage до того, как компонент будет уничтожен из-за обновления. Затем, когда вы пытаетесь получить данные из службы, если они будут пустыми, попробуйте получить их из localStorage.

export class SummaryComponent implements OnInit, OnDestroy {

   salutation: string;
   firstName: string;

constructor(
  private router: Router,
  public userService: UserService
) {
  }

 ngOnInit(): void {
   this.userService.salutation.subscribe(c => { this.salutation = c || localStorage.get('salutation'); });
   this.userService.firstName.subscribe(c => { this.firstName = c || localStorage.get('firstName'); });
 }

 ngOnDestroy(): void {
   localStorage.setItem('salutation', this.salutation));
   localStorage.setItem('firstName', this.firstName));
 }

}

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

РЕДАКТИРОВАТЬ

Извините, вы правы, что ngOnDestory не будет запускаться при обновлении страницы, для этого вам нужно будет обработать window: beforeunload.

  @HostListener('window:beforeunload', ['$event'])
  beforeunload($event: Event): void {
   // set localStorage here
  }  

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

goToDetails2(form : NgForm) {
          this.userService.salutation = new BehaviorSubject(form.value.salutation);
          this.userService.firstName = new BehaviorSubject(form.value.firstName);
          localStorage.setItem('salutation', form.value.salutation));
          localStorage.setItem('firstName', form.value.firstName));
      }

РЕДАКТИРОВАТЬ-2 к вашей проблеме, описанной в комментарии

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

@Injectable({
  providedIn: 'root'
})

export class UserService {

    //Details Page 1
    public salutation: BehaviorSubject<string> = new BehaviorSubject('');
    public firstName: BehaviorSubject<string> = new BehaviorSubject('');

Затем в своем Details1Component установите для них значения, используя следующий

goToDetails2(form : NgForm) {
          this.userService.salutation.next(form.value.salutation);
          this.userService.firstName.next(form.value.firstName);
          localStorage.setItem('salutation', form.value.salutation));
          localStorage.setItem('firstName', form.value.firstName));
      }
person Stefan    schedule 24.03.2020
comment
Привет, Стефан, Посмотрел результаты: 1) ngOnDestroy () не вызывается при обновлении, но когда я нажимаю кнопку возврата браузера w chrome. Итак, salutation.subscribe (c = ›{this.salutation = c || localStorage. getItem ('приветствие');}) не работает при обновлении (2) добавленная логика localStorage.setItem ('приветствие', this.salutation)); в компоненте page1. Затем пользователь заполняет форму и переходит на страницу сводки. Alert () в 1-й строке ngOnInit на сводной странице подтверждает, что данные получены из localstrorage, но salutation.subscribe (c = ›{this.salutation = c || localStorage.get ('salutation');}) не отображает данные. - person Mega; 25.03.2020
comment
Установка данных в компонентах страницы 1 делает данные доступными на сводной странице с помощью localStorage.getItem (). Но после обновления salutation.subscribe (c = ›{this.salutation = c || localStorage.get ('salutation');}); Не работает. Похоже, что обновление удаляет то, что было ранее опубликовано. - person Mega; 25.03.2020
comment
Не могли бы вы добавить console.log (localStorage.get ('salutation')) в свою подписку, когда вы прошли весь путь от первого шага до сводки, а затем обновили страницу? Также вы можете посмотреть, что установлено в вашем локальном хранилище, с помощью инструментов разработчика браузера. - person Stefan; 25.03.2020
comment
Доброе утро, Стефан, в консоли появляется ошибка. Ошибка типа: невозможно прочитать свойство «подписаться» неопределенного значения в SummaryComponent.ngOnInit (summary.component.ts: 67) Проблема после обновления this.userService.salutation не определена. Похоже, переменные службы данных необходимо повторно инициализировать после обновления страницы. В качестве итогового компонента я определил var underfined; а затем проверил, если (this.userService.salutation == undefined) {this.userService = new UserService (..). Но у UserService есть конструктор (частный http: HttpClient). Выглядит беспорядочно. Что ты посоветуешь? Мне нужно повторно запустить службу - person Mega; 25.03.2020
comment
Спасибо за ваш комментарий и отредактированный пост. Я думаю, что успешно устанавливаю переменные в локальном хранилище. Проблема в том, что переменная UserService отображается как неопределенная, как описано выше. Мне нужно повторно инициализировать службу? Как это лучше всего сделать с аргументом HttpClient? Это хороший подход? - person Mega; 25.03.2020
comment
Привет, даже после того, как я вручную инициализировал переменные службы данных в сводном компоненте следующим образом: var undefined; если (this.userService.salutation == undefined) {this.userService.salutation = новый BehaviorSubject (localStorage.getItem ('приветствие')); this.userService.firstName = новый BehaviorSubject (localStorage.getItem ('firstName')); } Поля формы не заполнены. Итак, похоже, у меня есть 2 проблемы: 1. Инициализация службы при обновлении 2. Заполнение полей страницы с помощью подписки, которая читает из локального хранилища. В локальном хранилище есть соответствующие данные. - person Mega; 25.03.2020
comment
Ух ты! Стефан. Большое спасибо. Инициализация моего BehaviorSubject непосредственно внутри службы сделала свое дело. Должен признаться, я не разбирался в RxJ, поведении и его отношении к Angular. Я знал об этом в последние несколько дней из блога infragistics.com/community/blogs/b/infragistics/posts/ при попытке поделиться данными между моими представлениями в angular9. Стефан! Я не буду возражать против хорошей и практичной ссылки на букварь от вас. Оставайся в безопасности и хорошо, брось! - person Mega; 26.03.2020
comment
Моя следующая задача - это вызов Https POST restful API через angular с использованием токена авторизации OAuth2. Я заставил его работать через почтальона, используя токен авторизации OAuth2, просто чтобы убедиться, что у меня есть рабочая полезная нагрузка запроса json и функциональный API. Теперь мне нужно воспроизвести это в том же приложении angular, то есть опубликовать детали, скомпилированные на трех страницах, в службу с заголовком OAuth2 Token. Пожалуйста, следите за пространством, и любые советы / ссылки также будут оценены. Спасибо. - person Mega; 26.03.2020