Принудительно MatSnackBar для обновления пользовательского интерфейса после получения обновления данных - PullRequest
0 голосов
/ 28 февраля 2020

Мое приложение использует MatSnackBar для отображения информации об ошибках для пользователя на основе результатов нескольких вызовов API, которые инициируются нажатием кнопки.

Поскольку вызовы API могут возвращаться из серверной части в В разное время нам нужен способ обновления данных Snackbars новой информацией при возврате вызовов API. Для этого рассматриваемый компонент пользовательского интерфейса имеет ссылку на SnackbarService, и когда API вызывает ошибку, подробности ответа передаются в SnackbarService.

Сам SnackbarService имеет запись обо всех ошибках, которые имеют была вытолкнута из пользовательского интерфейса _snackbarData, эта информация проталкивается через объект BehaviourSubject, который предоставляется снэк-панели через его конфигурацию Когда компонент пользовательского интерфейса передает информацию об ошибках в SnackbarService, он затем проталкивается через BehaviourSubject, а на панели с закусками появляется актуальный список всех ошибок, которые необходимо отобразить.

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

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

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

Могу ли я заставить пользовательский интерфейс снэк-бара обновляться при получении новых данных?

UI Component

ngOnInit() {

this._genericErrorService.genericErrorSubject$.pipe(takeUntil(this.destroy$)).subscribe((errorDetails: ErrorDetails) => {
      this.syncingClasses = false;
      console.log("Pushed error");
      this._errorSnackbarService.pushErrorToSnackbar(errorDetails);
    });
} 

SnackbarService

export class ErrorSnackbarService {

  private _snackbarData: ErrorDetails[];
  errorDetailsSubject: BehaviorSubject<ErrorDetails[]> = new BehaviorSubject<ErrorDetails[]>(this._snackbarData);

  constructor(private _snackbar: MatSnackBar) {
    this._snackbarData = [];
  }

  pushErrorToSnackbar(errorDetails: ErrorDetails) {
    this.appendSnackbarData(errorDetails);
    this.errorDetailsSubject.next(this._snackbarData);
    if(!this.isSnackbarActive()) {
      this._snackbar.dismiss();
      this.displaySnackbar();
    } else { //This else causes the errors to be displayed as expected, without it the snackbar doesn't update. 
      this._snackbar.dismiss();
      this.displaySnackbar();
    }
  }
}

  private displaySnackbar() {
    this._snackbar.openFromComponent(ErrorSnackbarComponent, {
      data: this.errorDetailsSubject,
      duration: 0,
    });

    this._snackbar._openedSnackBarRef.afterDismissed().pipe(take(1)).subscribe(res => {
      this.clearSnackbar();
    })
  }

  private appendSnackbarData(errorDetails: ErrorDetails) {
    this._snackbarData.push(errorDetails);
  }

  clearSnackbar() {
    this._snackbarData = [];
  }

  dismissSnackbar() {
    this._snackbar.dismiss();
  }
}

Snackbar Component

export class ErrorSnackbarComponent extends AbstractBaseComponent implements OnInit {

  private _errorList: ErrorDetails[];

  constructor(baseService: BaseService,
              private _matSnackBarRef: MatSnackBarRef<ErrorSnackbarComponent>,
              @Inject(MAT_SNACK_BAR_DATA) public data: BehaviorSubject<ErrorDetails[]>) {
    super(baseService);
  }

  ngOnInit() {
    this.data.subscribe(res => {
      console.log("errorList: ", res);
      this._errorList = res;


    })
  }

  dismiss() {
    this._matSnackBarRef.dismiss();
  }

}

Шаблон Snackbar

<div (click)="dismiss()" class="pointer">
  <div class="row-container">
    <div class="row" *ngFor="let error of _errorList">
      <div class="col-1 error-icon-container">
        <mat-icon *ngIf="!isWarning(error.errorKey)" class="error-icon">cancel</mat-icon>
        <mat-icon *ngIf="isWarning(error.errorKey)" class="warning-icon">warning</mat-icon>
      </div>
      <div class="col-11 margin-auto">
      <span class="snackbar-text">
        {{ getTranslateKey("errors." + error.errorKey) | translate }}
      </span>
      </div>
    </div>
  </div>
</div>

1 Ответ

0 голосов
/ 28 февраля 2020

Вы можете обновить существующий компонент SnackBar, сохранив ссылку и затем применив значение к экземпляру. Например, используя Angular SnackBar Pizza Пример:

import {Component, OnInit} from '@angular/core';
import {MatSnackBar, MatSnackBarRef} from '@angular/material/snack-bar';

/**
 * @title Snack-bar with a custom component
 */
@Component({
  selector: 'snack-bar-component-example',
  templateUrl: 'snack-bar-component-example.html',
  styleUrls: ['snack-bar-component-example.css'],
})
export class SnackBarComponentExample implements OnInit {
  durationInSeconds = 50;
  currentSnackBar: MatSnackBarRef<PizzaPartyComponent>;

  constructor(private _snackBar: MatSnackBar) {}

  ngOnInit() {
    setInterval( () => {
      if (this.currentSnackBar) {
        this.currentSnackBar.instance.currentTime += 1
      }
    }, 1000);
  }

  openSnackBar() {
    this.currentSnackBar = this._snackBar.openFromComponent(PizzaPartyComponent, {
      duration: this.durationInSeconds * 1000,
    });
  }

}


@Component({
  selector: 'snack-bar-component-example-snack',
  templateUrl: 'snack-bar-component-example-snack.html',
  styles: [`
    .example-pizza-party {
      color: hotpink;
    }
  `],
})
export class PizzaPartyComponent {
  currentTime = 0;
}

Пример работы StackBlitz: https://stackblitz.com/edit/angular-txcovs

Общая идея заключается в том, что при открытии компонента, вам нужно сохранить эту ссылку Snack Bar, а затем применить любые изменения к этому указанному c Reference.

...