Angular 结合 canvas 元素不遵守 OnChanges
Angular in combination with canvas element not respecting OnChanges
查看此 stackblitz:https://stackblitz.com/edit/angular-ivy-wecw7p?file=src%2Fapp%2Fcanvas%2Fcanvas.component.ts
我有一个包含子 CanvasComponent 的常规 AppComponent(使用 'chord' 选择器)。应用程序组件创建一个 Chord 对象并将其传递给子 canvas 组件:
<chord [chord]=chordData></chord>
Chord
接口目前只有一个 Name
字符串 属性,该名称使用 <input>
字段显示在 AppComponent 中,并呈现在canvas 组件使用 {{chord.Name}}
。
到目前为止,还不错。
在我的 canvas 组件中,我还渲染了一个 <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);
}
}
}
}
问题是,当我使用输入字段更新和弦名称时,canvas 组件中的名称也会更改,但不会在 canvas 元素内部更改。
这是因为 canvas 需要重新绘制,很公平。我已经实现了 OnChanges
,我将需要重新绘制我的 canvas,但它没有以任何方式被击中。
如何确保在更新父 Chord 对象时,canvas 也会重绘?
也欢迎任何有关代码改进的提示,只是从 Angular :)
开始
方法 ngOnChanges 没有被触发,因为和弦输入值永远不会改变。当您更新名称时,您可能只是在 Chord 对象上设置 属性,而不是更改对象本身。 Angular 不会捕捉到那个变化,因为它仍然是同一个对象。
如果您只是将名称作为输入传递,那么应该可以正常工作,并且您应该会看到更新,因为字符串是不可变的并且会创建一个新对象。或者,当外部组件中的输入值发生变化时,您可以使用对象扩展语法更新和弦的值:this.chordOuter = { ...this.chordOuter };
。这应该会导致将新值设置为和弦 属性.
点差示例
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 创建一个可观察对象,然后使用 async[=35 将其绑定到您的组件=] 管道.
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>
查看此 stackblitz:https://stackblitz.com/edit/angular-ivy-wecw7p?file=src%2Fapp%2Fcanvas%2Fcanvas.component.ts
我有一个包含子 CanvasComponent 的常规 AppComponent(使用 'chord' 选择器)。应用程序组件创建一个 Chord 对象并将其传递给子 canvas 组件:
<chord [chord]=chordData></chord>
Chord
接口目前只有一个 Name
字符串 属性,该名称使用 <input>
字段显示在 AppComponent 中,并呈现在canvas 组件使用 {{chord.Name}}
。
到目前为止,还不错。
在我的 canvas 组件中,我还渲染了一个 <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);
}
}
}
}
问题是,当我使用输入字段更新和弦名称时,canvas 组件中的名称也会更改,但不会在 canvas 元素内部更改。
这是因为 canvas 需要重新绘制,很公平。我已经实现了 OnChanges
,我将需要重新绘制我的 canvas,但它没有以任何方式被击中。
如何确保在更新父 Chord 对象时,canvas 也会重绘?
也欢迎任何有关代码改进的提示,只是从 Angular :)
开始方法 ngOnChanges 没有被触发,因为和弦输入值永远不会改变。当您更新名称时,您可能只是在 Chord 对象上设置 属性,而不是更改对象本身。 Angular 不会捕捉到那个变化,因为它仍然是同一个对象。
如果您只是将名称作为输入传递,那么应该可以正常工作,并且您应该会看到更新,因为字符串是不可变的并且会创建一个新对象。或者,当外部组件中的输入值发生变化时,您可以使用对象扩展语法更新和弦的值:this.chordOuter = { ...this.chordOuter };
。这应该会导致将新值设置为和弦 属性.
点差示例
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 创建一个可观察对象,然后使用 async[=35 将其绑定到您的组件=] 管道.
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>