Commit 9a63b6d8 authored by Alexander Philipp Nowosad's avatar Alexander Philipp Nowosad
Browse files

Add changes and loader to canvas definition

parent 34d69d74
import { PouchdbModelPart } from '../database/pouchdb-model-part';
import { Equality } from '../shared/equality';
export class CanvasDefinitionCell implements PouchdbModelPart {
export class CanvasDefinitionCell
implements PouchdbModelPart, Equality<CanvasDefinitionCell>
{
isSpacer: boolean;
id?: string;
name?: string;
......@@ -20,4 +23,16 @@ export class CanvasDefinitionCell implements PouchdbModelPart {
colspan: this.colspan,
};
}
equals(other: CanvasDefinitionCell): boolean {
if (other == null) {
return false;
}
return (
this.isSpacer === other.isSpacer &&
this.name === other.name &&
this.rowspan === other.rowspan &&
this.colspan === other.colspan
);
}
}
import { Injectable } from '@angular/core';
import { PouchdbService } from '../database/pouchdb.service';
import PouchDB from 'pouchdb-browser';
import { CanvasDefinition } from './canvas-definition';
import { CanvasDefinitionCell } from './canvas-definition-cell';
import { getId } from '../model/utils';
import { ElementService } from '../database/element.service';
import { Observable } from 'rxjs';
import { DefaultElementService } from '../database/default-element.service';
@Injectable({
providedIn: 'root',
})
export class CanvasDefinitionService
implements ElementService<CanvasDefinition>
{
constructor(private pouchdbService: PouchdbService) {}
add(canvasDefinition: Partial<CanvasDefinition>) {
return this.pouchdbService.post(new CanvasDefinition(canvasDefinition));
}
getList() {
return this.pouchdbService.find<CanvasDefinition>(
CanvasDefinition.typeName,
{ selector: {} }
);
}
get(id: string) {
return this.pouchdbService
.get(id)
.then((definition) => new CanvasDefinition(definition));
}
getChangesFeed(id: string): Observable<any> {
return this.pouchdbService.getChangesFeed(id);
export class CanvasDefinitionService extends DefaultElementService<CanvasDefinition> {
protected get typeName(): string {
return CanvasDefinition.typeName;
}
updateRows(
canvasDefinition: CanvasDefinition,
newRows: CanvasDefinitionCell[][]
) {
async updateRows(id: string, newRows: CanvasDefinitionCell[][]) {
const canvasDefinition = await this.get(id);
const ids = new Set<string>();
newRows.forEach((row) =>
row
......@@ -54,15 +28,18 @@ export class CanvasDefinitionService
})
);
canvasDefinition.rows = newRows;
return this.save(canvasDefinition);
}
delete(id: string) {
return this.pouchdbService
.get(id)
.then((result) => this.pouchdbService.remove(result));
async update(id: string, element: Partial<CanvasDefinition>): Promise<any> {
const dbElement = await this.get(id);
dbElement.update(element);
return this.save(dbElement);
}
save(canvasDefinition: CanvasDefinition): Promise<PouchDB.Core.Response> {
return this.pouchdbService.put(canvasDefinition);
protected createElement(
element: Partial<CanvasDefinition>
): CanvasDefinition {
return new CanvasDefinition(element);
}
}
......@@ -17,6 +17,10 @@ export class CanvasDefinition extends PouchdbModel {
);
}
update(canvasDefinition: Partial<CanvasDefinition>) {
Object.assign(this, canvasDefinition);
}
get rootFeatures(): Feature[] {
const rootFeatures: Feature[] = [];
this.rows.forEach((row) =>
......
<form [formGroup]="form" (ngSubmit)="emitSubmitForm()">
<div class="form-group form-row">
<label for="name" class="col-sm-4 col-form-label">Name</label>
<div class="col-sm-8">
<input
type="text"
formControlName="name"
class="form-control"
[class.is-invalid]="nameControl.invalid && nameControl.touched"
id="name"
/>
</div>
</div>
<div class="form-group row">
<div class="col-sm-12">
<button
type="submit"
class="btn btn-sm btn-block"
[class.btn-dark]="!changed"
[class.btn-primary]="changed"
[disabled]="!form.valid"
>
Update Canvas Definition<span *ngIf="changed"> (unsaved changes)</span>
</button>
</div>
</div>
</form>
import {
Component,
EventEmitter,
Input,
OnChanges,
OnDestroy,
OnInit,
Output,
SimpleChanges,
} from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Subscription } from 'rxjs';
import { debounceTime, tap } from 'rxjs/operators';
import { CanvasDefinition } from '../../../canvas-meta-model/canvas-definition';
@Component({
selector: 'app-canvas-definition-form',
templateUrl: './canvas-definition-form.component.html',
styleUrls: ['./canvas-definition-form.component.css'],
})
export class CanvasDefinitionFormComponent
implements OnInit, OnChanges, OnDestroy
{
@Input() canvasDefinition: CanvasDefinition;
@Output() submitForm = new EventEmitter<FormGroup>();
form: FormGroup = this.fb.group({
name: ['', Validators.required],
});
changed = false;
private changeSubscription: Subscription;
constructor(private fb: FormBuilder) {}
ngOnInit() {
this.changeSubscription = this.form.valueChanges
.pipe(
debounceTime(300),
tap(
(value) =>
(this.changed = !this.equalCanvasDefinitions(
this.canvasDefinition,
value
))
)
)
.subscribe();
}
ngOnChanges(changes: SimpleChanges) {
if (changes.canvasDefinition) {
const newCanvasDefinition = changes.canvasDefinition.currentValue;
const oldCanvasDefinition = changes.canvasDefinition.previousValue;
if (
!this.equalCanvasDefinitions(oldCanvasDefinition, newCanvasDefinition)
) {
this.loadForm(newCanvasDefinition);
}
}
}
ngOnDestroy() {
if (this.changeSubscription) {
this.changeSubscription.unsubscribe();
}
}
emitSubmitForm() {
this.submitForm.emit(this.form);
}
private loadForm(canvasDefinition: CanvasDefinition) {
if (canvasDefinition != null) {
this.form.setValue({ name: canvasDefinition.name });
} else {
this.form.setValue({ name: '' });
}
}
private equalCanvasDefinitions(
canvasDefinitionA: CanvasDefinition,
canvasDefinitionB: CanvasDefinition
) {
if (canvasDefinitionA == null && canvasDefinitionB == null) {
return true;
}
if (canvasDefinitionA == null || canvasDefinitionB == null) {
return false;
}
return canvasDefinitionA.name === canvasDefinitionB.name;
}
get nameControl() {
return this.form.get('name');
}
}
import { Injectable } from '@angular/core';
import { ElementLoaderService } from '../../database/element-loader.service';
import { ActivatedRoute, ParamMap } from '@angular/router';
import { CanvasDefinition } from '../../canvas-meta-model/canvas-definition';
import { CanvasDefinitionService } from '../../canvas-meta-model/canvas-definition.service';
@Injectable()
export class CanvasDefinitionLoaderService extends ElementLoaderService {
canvasDefinition: CanvasDefinition = null;
constructor(
private canvasDefinitionService: CanvasDefinitionService,
route: ActivatedRoute
) {
super(route);
}
protected initParams(paramMap: ParamMap) {
const canvasDefinitionId = paramMap.get('id');
this.changesFeed = this.canvasDefinitionService
.getChangesFeed(canvasDefinitionId)
.subscribe(() => this.loadCanvasDefinition(canvasDefinitionId));
this.loadCanvasDefinition(canvasDefinitionId).then();
}
private async loadCanvasDefinition(canvasDefinitionId: string) {
this.canvasDefinition = await this.canvasDefinitionService.get(
canvasDefinitionId
);
this.elementLoaded();
}
}
<form [formGroup]="form" (ngSubmit)="emitSubmitForm()">
<button
type="button"
class="btn btn-block btn-sm btn-primary mb-3"
(click)="addRow()"
>
Add row
</button>
<ul formArrayName="rows" class="list-group">
<li
class="list-group-item"
*ngFor="let row of rowsFormArray.controls; let rowIndex = index"
[formArrayName]="rowIndex.toString()"
>
<div class="d-flex">
<b>{{ rowIndex + 1 }}. Row</b>
<button
type="button"
class="btn btn-xs btn-primary ml-auto"
(click)="addBlock(rowIndex)"
>
Add Block
</button>
<button
type="button"
class="btn btn-xs btn-secondary ml-3"
(click)="addRow(rowIndex)"
>
Add Row above
</button>
<button
type="button"
class="btn btn-xs btn-dark ml-3"
(click)="removeRow(rowIndex)"
>
Remove
</button>
</div>
<div *ngIf="asFormArray(row).length > 0" class="form-group mt-2">
<div
class="form-row"
*ngFor="
let block of asFormArray(row).controls;
let blockIndex = index
"
[formGroupName]="blockIndex.toString()"
>
<div class="form-group col-md-3">
<label for="selectSpacer{{ blockIndex }}">Element or Spacer</label>
<select
id="selectSpacer{{ blockIndex }}"
class="form-control"
formControlName="isSpacer"
(change)="spacerChange(rowIndex, blockIndex)"
>
<option [ngValue]="false">Element</option>
<option [ngValue]="true">Spacer</option>
</select>
</div>
<div class="col-md-3" *ngIf="block.value.isSpacer"></div>
<div class="form-group col-md-3" *ngIf="!block.value.isSpacer">
<label for="inputName{{ blockIndex }}">Name</label>
<input
class="form-control"
id="inputName{{ blockIndex }}"
formControlName="name"
/>
</div>
<div class="form-group col-md-3">
<label for="inputRowspan{{ blockIndex }}">Rowspan</label>
<input
type="number"
[min]="1"
class="form-control"
id="inputRowspan{{ blockIndex }}"
formControlName="rowspan"
/>
</div>
<div class="form-group col-md-3">
<label for="inputColspan{{ blockIndex }}">Colspan</label>
<div class="input-group">
<input
type="number"
[min]="1"
class="form-control"
id="inputColspan{{ blockIndex }}"
formControlName="colspan"
/>
<div class="input-group-append">
<button
type="button"
class="btn btn-dark form-control"
(click)="removeBlock(rowIndex, blockIndex)"
>
Remove
</button>
</div>
</div>
</div>
</div>
</div>
</li>
</ul>
<button
type="submit"
class="btn btn-sm btn-block mt-3"
[class.btn-dark]="!changed"
[class.btn-primary]="changed"
[disabled]="!form.valid"
>
Update<span *ngIf="changed"> (unsaved changes)</span>
</button>
</form>
import {
Component,
EventEmitter,
Input,
OnChanges,
OnDestroy,
OnInit,
Output,
SimpleChanges,
} from '@angular/core';
import {
AbstractControl,
FormArray,
FormBuilder,
FormGroup,
} from '@angular/forms';
import { Subscription } from 'rxjs';
import { debounceTime, tap } from 'rxjs/operators';
import { CanvasDefinitionRowFormService } from '../../form-services/canvas-definition-row-form.service';
import { equalsListOfLists } from '../../../shared/utils';
import { CanvasDefinitionCell } from '../../../canvas-meta-model/canvas-definition-cell';
@Component({
selector: 'app-canvas-definition-row-form',
templateUrl: './canvas-definition-row-form.component.html',
styleUrls: ['./canvas-definition-row-form.component.css'],
})
export class CanvasDefinitionRowFormComponent
implements OnInit, OnChanges, OnDestroy
{
@Input() canvasDefinitionRows: CanvasDefinitionCell[][];
@Output() submitForm = new EventEmitter<FormArray>();
form: FormGroup = this.fb.group({
rows: this.fb.array([]),
});
changed = false;
private changeSubscription: Subscription;
constructor(
private canvasDefinitionRowFormService: CanvasDefinitionRowFormService,
private fb: FormBuilder
) {}
ngOnInit() {
this.changeSubscription = this.form.valueChanges
.pipe(
debounceTime(300),
tap(
(value) =>
(this.changed = !this.equalCanvasDefinitionRows(
this.canvasDefinitionRows,
this.canvasDefinitionRowFormService.get(value.rows)
))
)
)
.subscribe();
}
ngOnChanges(changes: SimpleChanges) {
if (changes.canvasDefinitionRows) {
const newCanvasDefinitionRows = changes.canvasDefinitionRows.currentValue;
const oldCanvasDefinitionRows =
changes.canvasDefinitionRows.previousValue;
if (
!this.equalCanvasDefinitionRows(
oldCanvasDefinitionRows,
newCanvasDefinitionRows
)
) {
this.loadForm(newCanvasDefinitionRows);
}
}
}
ngOnDestroy() {
if (this.changeSubscription) {
this.changeSubscription.unsubscribe();
}
}
emitSubmitForm() {
this.submitForm.emit(this.rowsFormArray);
}
addRow(index: number = null) {
if (index == null) {
this.rowsFormArray.push(this.fb.array([]));
} else {
this.rowsFormArray.insert(index, this.fb.array([]));
}
}
removeRow(index: number) {
this.rowsFormArray.removeAt(index);
}
addBlock(row: number) {
this.getRow(row).push(
this.canvasDefinitionRowFormService.createCanvasDefinitionCellForm()
);
}
spacerChange(row: number, index: number) {
const form = this.getRow(row).at(index) as FormGroup;
this.canvasDefinitionRowFormService.convertCanvasDefinitionCellForm(
form,
form.value.isSpacer
);
}
removeBlock(row: number, index: number) {
this.getRow(row).removeAt(index);
}
getRow(index: number): FormArray {
return this.rowsFormArray.at(index) as FormArray;
}
asFormArray(form: AbstractControl): FormArray {
return form as FormArray;
}
private loadForm(canvasDefinitionRows: CanvasDefinitionCell[][]) {
this.form.setControl(
'rows',
this.canvasDefinitionRowFormService.createForm(canvasDefinitionRows)
);
}
private equalCanvasDefinitionRows(
canvasDefinitionRowsA: CanvasDefinitionCell[][],
canvasDefinitionRowsB: CanvasDefinitionCell[][]
) {
if (canvasDefinitionRowsA == null && canvasDefinitionRowsB == null) {
return true;
}
if (canvasDefinitionRowsA == null || canvasDefinitionRowsB == null) {
return false;
}
return equalsListOfLists(canvasDefinitionRowsA, canvasDefinitionRowsB);
}
get nameControl() {
return this.form.get('name');
}
get rowsFormArray(): FormArray {
return this.form.get('rows') as FormArray;
}
}
......@@ -12,126 +12,24 @@
<main *ngIf="canvasDefinition" role="main" class="container">
<div class="my-3 p-3 bg-white rounded shadow-sm">
<h6 class="border-bottom border-gray pb-2">Canvas Definition</h6>
<form [formGroup]="rowForm" (ngSubmit)="submitRowForm()">
<button
type="button"
class="btn btn-block btn-sm btn-primary mb-3"
(click)="addRow()"
>
Add row
</button>
<ul formArrayName="rows" class="list-group">
<li
class="list-group-item"
*ngFor="let row of rowsFormArray.controls; let rowIndex = index"
[formArrayName]="rowIndex.toString()"
>
<div class="d-flex">
<b>{{ rowIndex + 1 }}. Row</b>
<button