Skip to content
Snippets Groups Projects
Commit 9dc8dbbd authored by jojohoch's avatar jojohoch
Browse files

[player] Fix ExpressionChangedAfterItHasBeenCheckedError in TextArea

- Replace UpdateTextareaPipe with DynamicRowsDirective

The dynamic height of the textarea is calculated based on the available
width. However, accessing the width of the textarea often results in an
error.
For this reason, the calculation is now done using resize and change
events in conjunction with a timeout to wait for the textarea to render.
parent f04dc5bc
No related branches found
No related tags found
No related merge requests found
Pipeline #46247 passed
import {
Component, Input, AfterViewInit
} from '@angular/core';
import { Component, Input } from '@angular/core';
import { TextAreaElement } from 'common/models/elements/input-elements/text-area';
import { delay, Observable, of } from 'rxjs';
import { TextInputComponent } from 'common/directives/text-input-component.directive';
@Component({
......@@ -33,11 +30,11 @@ import { TextInputComponent } from 'common/directives/text-input-component.direc
autocorrect="off"
spellcheck="false"
value="{{elementModel.value}}"
[rows]="(isViewInitialized | async) && elementModel.hasDynamicRowCount ?
(elementModel.expectedCharactersCount | updateTextareaRows:
input.offsetWidth:
elementModel.styling.fontSize) :
elementModel.rowCount"
dynamicRows
[expectedCharactersCount]="elementModel.expectedCharactersCount"
[fontSize]="elementModel.styling.fontSize"
(dynamicRowsChange)="dynamicRows = $event"
[rows]="elementModel.hasDynamicRowCount && dynamicRows ? dynamicRows : elementModel.rows"
[attr.inputmode]="elementModel.showSoftwareKeyboard ? 'none' : 'text'"
[formControl]="elementFormControl"
[readonly]="elementModel.readOnly"
......@@ -59,12 +56,7 @@ import { TextInputComponent } from 'common/directives/text-input-component.direc
':host ::ng-deep .no-label .mat-form-field-outline-gap {border-top-color: unset !important}'
]
})
export class TextAreaComponent extends TextInputComponent implements AfterViewInit {
export class TextAreaComponent extends TextInputComponent {
@Input() elementModel!: TextAreaElement;
isViewInitialized: Observable<boolean> = of(false);
ngAfterViewInit(): void {
this.isViewInitialized = of(true).pipe(delay(0));
}
dynamicRows: number = 0;
}
import {
AfterViewInit, Directive, ElementRef, EventEmitter, HostListener, Input, OnChanges, Output, SimpleChanges
} from '@angular/core';
@Directive({
selector: '[dynamicRows]'
})
export class DynamicRowsDirective implements AfterViewInit, OnChanges {
@Input() fontSize!: number;
@Input() expectedCharactersCount!: number;
@Output() dynamicRowsChange: EventEmitter<number> = new EventEmitter<number>();
@HostListener('window:resize') onResize() {
// guard against resize before view is rendered
this.calculateDynamicRows();
}
constructor(public elementRef: ElementRef) {}
ngAfterViewInit(): void {
this.calculateDynamicRows();
}
calculateDynamicRows(): void {
// give the textarea time to render before calculating the dynamic row count
setTimeout(() => {
const averageCharWidth = this.fontSize / 2;
if (this.elementRef.nativeElement.offsetWidth) {
this.dynamicRowsChange.emit(
Math.ceil((
this.expectedCharactersCount * averageCharWidth) /
this.elementRef.nativeElement.offsetWidth
)
);
}
});
}
ngOnChanges(changes: SimpleChanges): void {
if (changes.fontSize || changes.expectedCharactersCount) {
this.calculateDynamicRows();
}
}
}
import { UpdateTextareaRowsPipe } from './update-textarea-rows.pipe';
describe('UpdateRowsPipe', () => {
it('create an instance', () => {
const pipe = new UpdateTextareaRowsPipe();
expect(pipe).toBeTruthy();
});
});
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'updateTextareaRows'
})
export class UpdateTextareaRowsPipe implements PipeTransform {
transform(expectedCharactersCount: number, inputWidth: number, fontSize: number): number {
const averageCharWidth = fontSize / 2; // s. AverageCharWidth of dotNet
return Math.ceil((expectedCharactersCount * averageCharWidth) / inputWidth);
}
}
// eslint-disable-next-line max-classes-per-file
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
......@@ -25,6 +26,7 @@ import { HotspotImageComponent } from 'common/components/input-elements/hotspot-
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { ScrollPagesPipe } from 'common/pipes/scroll-pages.pipe';
import { MathEditorModule } from 'common/math-editor.module';
import { DynamicRowsDirective } from 'common/directives/dynamic-rows.directive';
import { TextComponent } from './components/text/text.component';
import { ButtonComponent } from './components/button/button.component';
import { TextFieldComponent } from './components/input-elements/text-field.component';
......@@ -71,7 +73,6 @@ import { GeometryComponent } from './components/geometry/geometry.component';
import { MathAtanPipe } from './pipes/math-atan.pipe';
import { MathDegreesPipe } from './pipes/math-degrees.pipe';
import { ArrayIncludesPipe } from './pipes/array-includes.pipe';
import { UpdateTextareaRowsPipe } from './pipes/update-textarea-rows.pipe';
import { SpinnerComponent } from './components/spinner/spinner.component';
import { GetValuePipe, MathFieldComponent } from './components/input-elements/math-field.component';
......@@ -138,10 +139,10 @@ import { GetValuePipe, MathFieldComponent } from './components/input-elements/ma
MathAtanPipe,
MathDegreesPipe,
ArrayIncludesPipe,
UpdateTextareaRowsPipe,
SpinnerComponent,
GetValuePipe,
MathFieldComponent
MathFieldComponent,
DynamicRowsDirective
],
exports: [
CommonModule,
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment