/* * Squidex Headless CMS * * @license * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ import { AbstractControl } from '@angular/forms'; import { BehaviorSubject, Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import { ErrorDto } from './utils/error'; import { Types } from './utils/types'; import { fullValue} from './angular/forms/forms-helper'; export interface FormState { submitted: boolean; error?: string | null; } export class Form { private readonly state = new State({ submitted: false }); public submitted = this.state.changes.pipe(map(s => s.submitted)); public error = this.state.changes.pipe(map(s => s.error)); constructor( public readonly form: T ) { } protected disable() { this.form.disable(); } protected enable() { this.form.enable(); } protected setValue(value?: V) { if (value) { this.form.reset(this.transformLoad(value)); } else { this.form.reset(); } } protected transformLoad(value: V): any { return value; } protected transformSubmit(value: any): V { return value; } public load(value: V | undefined) { this.state.next(_ => ({ submitted: false, error: null })); this.setValue(value); } public submit(): V | null { this.state.next(_ => ({ submitted: true })); if (this.form.valid) { const value = this.transformSubmit(fullValue(this.form)); this.disable(); return value; } else { return null; } } public submitCompleted(newValue?: V) { this.state.next(_ => ({ submitted: false, error: null })); this.enable(); if (newValue) { this.setValue(newValue); } else { this.form.markAsPristine(); } } public submitFailed(error?: string | ErrorDto) { this.state.next(_ => ({ submitted: false, error: this.getError(error) })); this.enable(); } private getError(error?: string | ErrorDto) { if (Types.is(error, ErrorDto)) { return error.displayMessage; } else { return error; } } } export function createModel(c: { new(): T; }, values: Partial): T { return Object.assign(new c(), values); } export class Model { public with(value: Partial, validOnly = false): T { return this.clone(value, validOnly); } protected clone(update: ((v: any) => V) | Partial, validOnly = false): V { let values: Partial; if (Types.isFunction(update)) { values = update(this); } else { values = update; } const clone = Object.assign(Object.create(Object.getPrototypeOf(this)), this); for (let key in values) { if (values.hasOwnProperty(key)) { let value = values[key]; if (value || !validOnly) { clone[key] = value; } } } if (Types.isFunction(clone.onCloned)) { clone.onCloned(); } return clone; } } export class ResultSet extends Model> { constructor( public readonly total: number, public readonly items: T[] ) { super(); } } export class State { private readonly state: BehaviorSubject>; private readonly initialState: Readonly; public get changes(): Observable> { return this.state; } public get snapshot(): Readonly { return this.state.value; } constructor(state: Readonly) { this.initialState = state; this.state = new BehaviorSubject(state); } public resetState() { this.next(this.initialState); } public next(update: ((v: T) => Readonly) | object) { if (Types.isFunction(update)) { this.state.next(update(this.state.value)); } else { this.state.next(Object.assign({}, this.snapshot, update)); } } }