Angular 在其自身范围内更新数据后不会更新 UI

问题描述 投票:0回答:1

我很确定我错过了一些东西。正如你在这里看到的,我只有一个订阅,而且在组件内部我使用 typeWriter 函数进行本地更改,但 UI 不会根据它进行更新。仅当我使用changeDetectorRef.detectChanges()时,它才起作用,当您进行本地更改时,它看起来不是最好的方法。

    import { Component, Input, OnDestroy, OnInit } from '@angular/core';
import { IconDefinition, faCopy } from '@fortawesome/free-solid-svg-icons';
import { HotToastService } from '@ngneat/hot-toast';
import { Store } from '@ngrx/store';
import { Subscription } from 'rxjs';
import { InitialState } from '../../../../../../store/store';
import { storeActions } from '../../../../../../store/store.actions';
import { selectPrinting } from '../../../../../../store/store.selectors';
import { MessageI } from './message.types';
@Component({
  selector: 'app-message',
  templateUrl: './message.component.html',
  styleUrl: './message.component.scss',
})
export class MessageComponent implements OnInit, OnDestroy {
  /**
   * Chat object which contains properties {from & message}
   */
  @Input() chat: MessageI;
  private subscriptions: Subscription[] = [];
  public printing$: boolean;

  public copyIcon: IconDefinition = faCopy;

  public copyIconStyle = {
    dimensions: {
      height: 20,
      width: 20,
    },
    style: { color: 'white', cursor: 'pointer', width: 'max-content' },
  };

  public setIntervalID: number;
  public printedMessage: string = '';

  get imageUrl(): string {
    return this.chat.from === 'You'
      ? 'assets/images/me.jpg'
      : 'assets/images/cat.png';
  }

  constructor(
    private toast: HotToastService,
    private store: Store<{ store: InitialState }>
  ) {}

  ngOnInit(): void {
    this.subscriptions.push(
      this.store.select(selectPrinting).subscribe((vl) => {
        this.printing$ = vl;
      })
    );

    if (this.chat.from === 'You') {
      this.printedMessage = this.chat.message;
      return;
    }

    this.typwriterEffect(50);

    this.store.dispatch(storeActions.printingHandler());
  }

  ngOnDestroy(): void {
    this.subscriptions.forEach((s) => s.unsubscribe());
  }

  copyMessage() {
    this.toast.success('Copied to clipboard!', {
      duration: 5000,
      style: {
        border: '1px solid #713200',
        padding: '16px',
        color: '#713200',
      },
      iconTheme: {
        primary: '#713200',
        secondary: '#FFFAEE',
      },
    });
    navigator.clipboard.writeText(this.chat.message);
  }

  typwriterEffect(speed: number) {
    // Has to be used from window otherwise theres typescript errors that it needs to be Nodejs.Timeout which prompts another error
    // conflicts with Nodejs enviroments
    this.setIntervalID = window.setInterval(() => {
      if (
        this.printedMessage.length !== this.chat.message.length &&
        this.printing$
      ) {
        this.printedMessage = this.chat.message.slice(
          0,
          this.printedMessage.length + 1
        );
      } else {
        this.store.dispatch(storeActions.printingHandler());
        clearInterval(this.setIntervalID);
      }
    }, speed);
  }
}
angular typescript rxjs angular-material ngrx
1个回答
0
投票

发生这种情况是因为当您的可观察对象触发时,未触发更改检测周期。
解决这个问题的一种方法是使用 async pipeline 来确保每次您的 observable 触发时都会触发更改检测。

在您的示例中,您可以执行以下操作:

    this.printing$ = this.store.select(selectPrinting)

代替:

    this.subscriptions.push(
      this.store.select(selectPrinting).subscribe((vl) => {
        this.printing$ = vl;
      })
    );

在 HTML 模板中:

<ng-container *ngIf="printing$ | async as printing">
// the HTML code where printing is used,
// you can reference to it in the template as `printing`
</ng-container>

另请注意,按照惯例,仅命名

Observables
并带有尾随“$”

© www.soinside.com 2019 - 2024. All rights reserved.