From 373108a5ff1bad9b941736455be924ff9a5fd829 Mon Sep 17 00:00:00 2001 From: rhenck <richard.henck@iqb.hu-berlin.de> Date: Wed, 10 Nov 2021 14:41:26 +0100 Subject: [PATCH] Refactor elements to use the model's value This change mainly helps the editor. The idea is to make the element components have the correct value of their model and don't rely on the formcontrol value. The formcontrol can be hard to manipulate, especially in compound elements. Setting the model value is way easier. The formcontrols are kept and should not interfere. --- .../element-components/checkbox.component.ts | 5 ++-- .../likert-radio-button-group.component.ts | 5 ++-- .../element-components/dropdown.component.ts | 7 +++-- .../radio-button-group.component.ts | 2 +- .../element-components/text-area.component.ts | 14 +++++----- .../text-field.component.ts | 17 +++++------ projects/editor/src/app/dialog.service.ts | 16 ++++++++--- .../canvas/canvas-element-overlay.ts | 28 ++++++++----------- .../element-properties.component.ts | 3 +- projects/editor/src/app/unit.service.ts | 19 +++++++++---- 10 files changed, 65 insertions(+), 51 deletions(-) diff --git a/projects/common/element-components/checkbox.component.ts b/projects/common/element-components/checkbox.component.ts index 839276244..1d5e2d79d 100644 --- a/projects/common/element-components/checkbox.component.ts +++ b/projects/common/element-components/checkbox.component.ts @@ -11,14 +11,15 @@ import { CheckboxElement } from '../models/checkbox-element'; [style.height.%]="100" [style.background-color]="elementModel.backgroundColor"> <mat-checkbox #checkbox class="example-margin" - (click)="elementModel.readOnly ? $event.preventDefault() : null" [formControl]="elementFormControl" + [checked]="$any(elementModel.value)" [style.color]="elementModel.fontColor" [style.font-family]="elementModel.font" [style.font-size.px]="elementModel.fontSize" [style.font-weight]="elementModel.bold ? 'bold' : ''" [style.font-style]="elementModel.italic ? 'italic' : ''" - [style.text-decoration]="elementModel.underline ? 'underline' : ''"> + [style.text-decoration]="elementModel.underline ? 'underline' : ''" + (click)="elementModel.readOnly ? $event.preventDefault() : null"> <div [style.line-height.%]="elementModel.lineHeight" [innerHTML]="elementModel.label"></div> </mat-checkbox> <mat-error *ngIf="elementFormControl.errors && elementFormControl.touched" diff --git a/projects/common/element-components/compound-elements/likert-radio-button-group.component.ts b/projects/common/element-components/compound-elements/likert-radio-button-group.component.ts index cc99f8085..0cc99391b 100644 --- a/projects/common/element-components/compound-elements/likert-radio-button-group.component.ts +++ b/projects/common/element-components/compound-elements/likert-radio-button-group.component.ts @@ -7,8 +7,9 @@ import { LikertElementRow } from '../../models/compound-elements/likert-element- selector: 'app-likert-radio-button-group', template: ` <mat-radio-group [style.display]="'grid'" - [style.grid-template-columns]="'5fr ' + '2fr '.repeat(elementModel.columnCount)" - [formControl]="elementFormControl"> + [formControl]="elementFormControl" + [value]="elementModel.value" + [style.grid-template-columns]="'5fr ' + '2fr '.repeat(elementModel.columnCount)"> <div [style.grid-column-start]="1" [style.grid-column-end]="2" [style.grid-row-start]="1" diff --git a/projects/common/element-components/dropdown.component.ts b/projects/common/element-components/dropdown.component.ts index 202d4f9df..66f3d33dc 100644 --- a/projects/common/element-components/dropdown.component.ts +++ b/projects/common/element-components/dropdown.component.ts @@ -17,9 +17,10 @@ import { DropdownElement } from '../models/dropdown-element'; [style.text-decoration]="elementModel.underline ? 'underline' : ''"> {{$any(elementModel).label}} </mat-label> - <mat-select [formControl]="elementFormControl"> - <mat-option *ngIf="elementModel.allowUnset" value="" (click)="$event.preventDefault()" - [style.pointer-events]="elementModel.readOnly ? 'none' : 'unset'"> + <mat-select [formControl]="elementFormControl" [value]="elementModel.value"> + <mat-option *ngIf="elementModel.allowUnset" value="" + [style.pointer-events]="elementModel.readOnly ? 'none' : 'unset'" + (click)="$event.preventDefault()"> </mat-option> <mat-option *ngFor="let option of elementModel.options; let i = index" [value]="i" [style.pointer-events]="elementModel.readOnly ? 'none' : 'unset'"> diff --git a/projects/common/element-components/radio-button-group.component.ts b/projects/common/element-components/radio-button-group.component.ts index 2a25eba1e..f16ee18a6 100644 --- a/projects/common/element-components/radio-button-group.component.ts +++ b/projects/common/element-components/radio-button-group.component.ts @@ -18,10 +18,10 @@ import { RadioButtonGroupElement } from '../models/radio-button-group-element'; <label id="radio-group-label" class="white-space-break" [innerHTML]="elementModel.label"> </label> -<!-- TODO why margin-bottom?--> <mat-radio-group aria-labelledby="radio-group-label" [fxLayout]="elementModel.alignment" [formControl]="elementFormControl" + [value]="elementModel.value" [style.margin-top.px]="10"> <mat-radio-button *ngFor="let option of elementModel.options; let i = index" [ngClass]="{ 'strike' : elementModel.strikeOtherOptions && diff --git a/projects/common/element-components/text-area.component.ts b/projects/common/element-components/text-area.component.ts index 5f9e485f2..7a8cf9347 100644 --- a/projects/common/element-components/text-area.component.ts +++ b/projects/common/element-components/text-area.component.ts @@ -15,16 +15,16 @@ import { TextAreaElement } from '../models/text-area-element'; [style.font-style]="elementModel.italic ? 'italic' : ''" [style.text-decoration]="elementModel.underline ? 'underline' : ''" [appearance]="$any(elementModel.appearance)"> - <textarea matInput [formControl]="elementFormControl" #input - rows="{{elementModel.rowCount}}" + <textarea matInput #input + autocomplete="off" rows="{{elementModel.rowCount}}" placeholder="{{elementModel.label}}" + [formControl]="elementFormControl" + [value]="elementModel.value" [readonly]="elementModel.readOnly" - (focus)="onFocus.emit(input)" - (blur)="onBlur.emit(input)" - autocomplete="off" - placeholder="{{elementModel.label}}" [style.min-width.%]="100" [style.line-height.%]="elementModel.lineHeight" - [style.resize]="elementModel.resizeEnabled ? 'both' : 'none'"> + [style.resize]="elementModel.resizeEnabled ? 'both' : 'none'" + (focus)="onFocus.emit(input)" + (blur)="onBlur.emit(input)"> </textarea> <mat-error *ngIf="elementFormControl.errors"> {{elementFormControl.errors | errorTransform: elementModel}} diff --git a/projects/common/element-components/text-field.component.ts b/projects/common/element-components/text-field.component.ts index 01fff83ee..ce0ac96c7 100644 --- a/projects/common/element-components/text-field.component.ts +++ b/projects/common/element-components/text-field.component.ts @@ -18,12 +18,13 @@ import { TextFieldElement } from '../models/text-field-element'; appInputBackgroundColor [backgroundColor]="elementModel.backgroundColor" [appearance]="$any(elementModel.appearance)"> <mat-label>{{elementModel.label}}</mat-label> - <input matInput type="text" [pattern]="elementModel.pattern" #input + <input matInput type="text" #input autocomplete="off" + [formControl]="elementFormControl" + [value]="elementModel.value" + [pattern]="elementModel.pattern" [readonly]="elementModel.readOnly" (focus)="onFocus.emit(input)" - (blur)="onBlur.emit(input)" - autocomplete="off" - [formControl]="elementFormControl"> + (blur)="onBlur.emit(input)"> <button *ngIf="elementModel.clearable" matSuffix mat-icon-button aria-label="Clear" (click)="onClick($event)"> <mat-icon>close</mat-icon> @@ -43,13 +44,13 @@ import { TextFieldElement } from '../models/text-field-element'; [style.text-decoration]="elementModel.underline ? 'underline' : ''" appInputBackgroundColor [backgroundColor]="elementModel.backgroundColor" [appearance]="$any(elementModel.appearance)"> - <input matInput type="text" #input + <input matInput type="text" #input autocomplete="off" + [formControl]="elementFormControl" + [value]="elementModel.value" [readonly]="elementModel.readOnly" [pattern]="elementModel.pattern" (focus)="onFocus.emit(input)" - (blur)="onBlur.emit(input)" - autocomplete="off" - [formControl]="elementFormControl"> + (blur)="onBlur.emit(input)"> <button *ngIf="elementModel.clearable" matSuffix mat-icon-button aria-label="Clear" (click)="onClick($event)"> <mat-icon>close</mat-icon> diff --git a/projects/editor/src/app/dialog.service.ts b/projects/editor/src/app/dialog.service.ts index 75cc5d3d9..7d12c70ee 100644 --- a/projects/editor/src/app/dialog.service.ts +++ b/projects/editor/src/app/dialog.service.ts @@ -76,12 +76,13 @@ export class DialogService { return dialogRef.afterClosed(); } - showLikertRowEditDialog(row: LikertElementRow): Observable<LikertElementRow> { + showLikertRowEditDialog(row: LikertElementRow, columns: LikertColumn[]): Observable<LikertElementRow> { const dialogRef = this.dialog.open(LikertRowEditDialog, { width: '300px', height: '550px', data: { - row: row + row: row, + columns: columns }, autoFocus: false }); @@ -327,14 +328,21 @@ export class LikertColumnEditDialog { <mat-label>ID</mat-label> <input #idField matInput type="text" [value]="data.row.id"> </mat-form-field> + <mat-select #valueField [value]="data.row.value"> + <mat-option *ngFor="let column of data.columns; let i = index" [value]="i"> + {{column.text}} + </mat-option> + </mat-select> </mat-dialog-content> <mat-dialog-actions> <button mat-button - [mat-dialog-close]="{ text: textField.value, id: idField.value }">{{'save' | translate }}</button> + [mat-dialog-close]="{ text: textField.value, id: idField.value, value: valueField.value }"> + {{'save' | translate }} + </button> <button mat-button mat-dialog-close>{{'cancel' | translate }}</button> </mat-dialog-actions> ` }) export class LikertRowEditDialog { - constructor(@Inject(MAT_DIALOG_DATA) public data: { row: LikertElementRow }) { } + constructor(@Inject(MAT_DIALOG_DATA) public data: { row: LikertElementRow, columns: LikertColumn[] }) { } } diff --git a/projects/editor/src/app/unit-view/page-view/canvas/canvas-element-overlay.ts b/projects/editor/src/app/unit-view/page-view/canvas/canvas-element-overlay.ts index fc70ebff1..cb7c58493 100644 --- a/projects/editor/src/app/unit-view/page-view/canvas/canvas-element-overlay.ts +++ b/projects/editor/src/app/unit-view/page-view/canvas/canvas-element-overlay.ts @@ -4,13 +4,11 @@ import { ViewChild, ViewContainerRef, OnInit, OnDestroy, ChangeDetectorRef } from '@angular/core'; import { Subject } from 'rxjs'; -import { takeUntil } from 'rxjs/operators'; import { UnitService } from '../../../unit.service'; import * as ElementFactory from '../../../../../../common/util/element.factory'; -import { FormElementComponent } from '../../../../../../common/form-element-component.directive'; import { ElementComponent } from '../../../../../../common/element-component.directive'; import { SelectionService } from '../../../selection.service'; -import { InputElement, UIElement, ValueChangeElement } from '../../../../../../common/models/uI-element'; +import { UIElement } from '../../../../../../common/models/uI-element'; @Directive() export abstract class CanvasElementOverlay implements OnInit, OnDestroy { @@ -36,21 +34,17 @@ export abstract class CanvasElementOverlay implements OnInit, OnDestroy { this.selectionService.selectElement({ componentElement: this, multiSelect: false }); - if (this.childComponent.instance instanceof FormElementComponent) { - this.childComponent.instance.formValueChanged - .pipe(takeUntil(this.ngUnsubscribe)) - .subscribe((changeElement: ValueChangeElement) => { - this.unitService.updateElementProperty([this.element], 'value', changeElement.values[1]); - }); + // This allows to listen for changed directly on the element. And update the model accordingly. + // Since the elements are no longer interactable this code is not used. It may be in the future that + // this functionality is wanted again. Therefore the code is kept in commented out form. - this.unitService.elementPropertyUpdated - .pipe(takeUntil(this.ngUnsubscribe)) - .subscribe(() => { - (this.childComponent.instance as FormElementComponent).updateFormValue( - (this.element as InputElement).value as string | number | boolean | null - ); - }); - } + // if (this.childComponent.instance instanceof FormElementComponent) { + // this.childComponent.instance.formValueChanged + // .pipe(takeUntil(this.ngUnsubscribe)) + // .subscribe((changeElement: ValueChangeElement) => { + // this.unitService.updateElementProperty([this.element], 'value', changeElement.values[1]); + // }); + // } } setSelected(newValue: boolean): void { diff --git a/projects/editor/src/app/unit-view/page-view/properties-panel/element-properties.component.ts b/projects/editor/src/app/unit-view/page-view/properties-panel/element-properties.component.ts index 2feab2233..c7ed4680d 100644 --- a/projects/editor/src/app/unit-view/page-view/properties-panel/element-properties.component.ts +++ b/projects/editor/src/app/unit-view/page-view/properties-panel/element-properties.component.ts @@ -155,7 +155,8 @@ export class ElementPropertiesComponent implements OnInit, OnDestroy { async editRowOption(optionIndex: number): Promise<void> { await this.unitService.editLikertRow( - (this.combinedProperties.rows as LikertElementRow[])[optionIndex] as LikertElementRow + (this.combinedProperties.rows as LikertElementRow[])[optionIndex] as LikertElementRow, + this.combinedProperties.columns as LikertColumn[] ); } } diff --git a/projects/editor/src/app/unit.service.ts b/projects/editor/src/app/unit.service.ts index 6da42f9e4..e0ef3f024 100644 --- a/projects/editor/src/app/unit.service.ts +++ b/projects/editor/src/app/unit.service.ts @@ -231,24 +231,31 @@ export class UnitService { }); } - async editLikertRow(question: LikertElementRow): Promise<void> { - await this.dialogService.showLikertRowEditDialog(question) + async editLikertRow(row: LikertElementRow, columns: LikertColumn[]): Promise<void> { + await this.dialogService.showLikertRowEditDialog(row, columns) .subscribe((result: LikertElementRow) => { if (result) { - if (result.id !== question.id) { + if (result.id !== row.id) { this.updateElementProperty( - [question], + [row], 'id', result.id ); } - if (result.text !== question.text) { + if (result.text !== row.text) { this.updateElementProperty( - [question], + [row], 'text', result.text ); } + if (result.value !== row.value) { + this.updateElementProperty( + [row], + 'value', + result.value + ); + } } }); } -- GitLab