0

看到这个堆栈闪电战:https ://stackblitz.com/edit/angular-ivy-wecw7p?file=src%2Fapp%2Fcanvas%2Fcanvas.component.ts

我有一个包含子 CanvasComponent 的常规 AppComponent(使用“和弦”选择器)。app 组件创建一个 Chord 对象并将其传递给子画布组件:

<chord [chord]=chordData></chord>

Chord接口目前只有一个Name字符串属性,该名称使用<input>字段显示在 AppComponent 中,并使用{{chord.Name}}. 到现在为止还挺好。

在我的画布组件中,我还渲染了一个<canvas>元素并在其中显示和弦名称。

import {
  Component,
  ViewChild,
  Input,
  ElementRef,
  OnChanges,
  SimpleChanges
} from '@angular/core';
import { Chord } from '../models/Chord';

@Component({
  selector: 'chord',
  template:
    '<div *ngIf="chord">{{chord.Name}}<br/><br/><canvas #canvas width=100 height=100></canvas></div>',
  styleUrls: ['./canvas.component.css']
})
export class CanvasComponent implements OnChanges {
  @Input() chord: Chord | undefined;
  @ViewChild('canvas') canvas: ElementRef<HTMLCanvasElement> | undefined;

  constructor() {}

  ngOnChanges(changes: SimpleChanges): void {
    console.log('changes in canvas');
    this.draw();
  }

  ngAfterViewInit(): void {
    this.draw();
  }

  private draw(): void {
    if (this.canvas) {
      let elmnt = this.canvas.nativeElement;
      let ctx = elmnt.getContext('2d');
      if (ctx && this.chord) {
        ctx.fillStyle = '#dddddd';
        ctx.fillRect(0,0,100,100);

        ctx.fillStyle = '#000000';
        ctx.font = '20px Arial';
        ctx.fillText(this.chord.Name, 20, 40);
      }
    }
  }
}

问题是,当我使用输入字段更新和弦名称时,画布组件中的名称也会发生变化,但不会在画布元素内部发生变化。

在此处输入图像描述

这是因为画布需要重新绘制,足够公平。我已经实现OnChanges了,我需要重新绘制我的画布,但它没有受到任何影响。

如何确保在更新父 Chord 对象时,画布也将被重绘?

并且欢迎任何关于代码改进的提示,刚开始使用 Angular :)

4

1 回答 1

2

ngOnChanges方法不会被触发,因为和弦输入值永远不会改变。更新名称时,您可能只是在 Chord 对象上设置属性,而不是更改对象本身。Angular 不会捕捉到这种变化,因为它仍然是同一个对象。

如果您只是将名称作为输入传递,那么应该可以正常工作,并且您应该会看到更新,因为字符串是不可变的并且创建了一个新对象。或者,当外部组件中的输入值发生变化时,您可以使用对象扩展语法更新 chord 的值:this.chordOuter = { ...this.chordOuter };. 这应该会导致为 chord 属性设置一个新值。

传播示例

class OuterComponent {
  chord: Chord = {};

  updateChord(partial: Partial<Chord>): void {
    this.chord = { ...this.chord, ...partial };
  }
}

模板

<input [ngModel]="chord.name" (ngModelChange)="updateChord({ name: $event })" />
<chord [chord]="chord"></chord>

反应形式

如果您计划使用响应式表单,您可以从valueChanges创建一个可观察对象,然后使用异步管道将其绑定到您的组件。

class OuterComponent {
   readonly form = new FormGroup({ name: new FormControl('') });
   readonly chord$ = this.form.valueChanges.pipe(map(x => this.convertToChord(x)));

   private convertToChord(value: any): Chord {
      /* Implement this yourself */
   }
}

模板

<chord [chord]="chord$ | async"></chord>
于 2021-07-19T15:22:20.630 回答