Page MenuHomeSealhub

mountable-with-fields.ts
No OneTemporary

mountable-with-fields.ts

import { ShapeToType } from "@sealcode/ts-predicates";
import { Context } from "koa";
import { FlatTemplatable, tempstream } from "tempstream";
import { FormControl } from "../forms/controls/form-control";
import { FieldsToShape, FormField } from "../forms/fields/field";
import { FormData, FormDataValue, FormMessage } from "../forms/form";
import { Mountable } from "./mountable";
export type PageErrorMessage = { type: "access" | "internal"; message: string };
export type Fields = Record<string, FormField<boolean, unknown>>;
export abstract class MountableWithFields<
F extends Fields = Fields
> extends Mountable {
fields: F;
field_names_prefix = ""; // useful for multiform, where many forms are merged into one and field assignment is made using the prefix
form_id = ""; // all fields within this mountable will be tied to form of this id
abstract controls: FormControl[];
constructor() {
super();
if (!this.fields) this.fields = {} as F;
}
init(): void {
for (const [name, field] of Object.entries(this.fields)) {
field.init(name);
}
}
// this one is meant to be overwritten
async validateValues(
ctx: Context,
data: Record<string, FormDataValue>
): Promise<{ valid: boolean; error: string }> {
return {
valid: true,
error: "",
};
}
async validate(
ctx: Context,
values: Record<string, FormDataValue>
): Promise<{
valid: boolean;
field_errors: Partial<Record<keyof Fields, string>>;
form_messages: FormMessage[];
}> {
const field_errors = {} as Record<keyof Fields, string>;
let valid = true;
const form_messages = [] as FormMessage[];
await Promise.all(
Object.entries(values)
.filter(([fieldname]) => fieldname in this.fields)
.map(async ([key]: [keyof F, FormDataValue]) => {
const field = this.fields[key];
const { valid: fieldvalid, message: fieldmessage } =
await field.getValue(ctx, values);
if (!fieldvalid) {
valid = false;
field_errors[field.name] = fieldmessage;
}
})
);
const formValidationResult = await this.validateValues(ctx, values);
if (!formValidationResult.valid) {
form_messages.push({
type: "error",
text: formValidationResult.error,
});
valid = false;
}
return { valid, field_errors, form_messages };
}
public renderControls(
ctx: Context,
data: FormData<keyof Fields>,
validate: boolean
): FlatTemplatable {
console.log("RENDER CONTROLS", data.raw_values);
return tempstream/* HTML */ `${this.controls.map((control) =>
control.render(
ctx,
data.raw_values,
data.messages,
this.field_names_prefix,
this.form_id,
validate
)
)}`;
}
async renderError(
_: Context,
error: PageErrorMessage
): Promise<FlatTemplatable> {
return error.message;
}
public renderMessages(
_: Context,
data: FormData<keyof Fields>
): FlatTemplatable {
return tempstream/* HTML */ `<div class="form-messages">
${data.messages.map(
(message) =>
`<div class="form-message form-message--${message.type}">${message.text}</div>`
)}
</div>`;
}
abstract extractRawValues(
ctx: Context
): Promise<Record<string, FormDataValue>>;
async getParsedValues(
ctx: Context
): Promise<ShapeToType<FieldsToShape<F>>> {
const raw_values = await this.extractRawValues(ctx);
const result: Record<string, unknown> = {};
const promises = Object.entries(this.fields).map(
async ([key, field]) => {
const { parsed } = await field.getValue(ctx, raw_values);
result[key] = parsed;
}
);
await Promise.all(promises);
return result as ShapeToType<FieldsToShape<F>>;
}
async getDatabaseValues(
ctx: Context
): Promise<ShapeToType<FieldsToShape<F>>> {
const raw_values = await this.extractRawValues(ctx);
const result: Record<string, unknown> = {};
const promises = Object.entries(this.fields).map(
async ([key, field]) => {
const db_value = await field.getDatabaseValue(ctx, raw_values);
if (db_value !== undefined) {
result[key] = db_value;
}
}
);
await Promise.all(promises);
return result as ShapeToType<FieldsToShape<F>>;
}
}

File Metadata

Mime Type
text/x-java
Expires
Mon, May 19, 00:57 (1 d, 20 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
661103
Default Alt Text
mountable-with-fields.ts (4 KB)

Event Timeline