Move JSON Schema logic to Declaform instead of making another component.

This commit is contained in:
Austin Smith
2025-11-18 09:25:35 -05:00
parent 0d4adef274
commit c8d52f15cc
6 changed files with 137 additions and 136 deletions

View File

@@ -1,3 +1,11 @@
import Ajv, { type ValidateFunction } from 'ajv';
import addFormats from 'ajv-formats';
const ajv = new Ajv({
coerceTypes: true,
});
addFormats(ajv);
interface FieldElement {
name: string;
type: string;
@@ -93,13 +101,17 @@ export class Declaform extends HTMLElement {
'src', // defines api endpoint used to hydrate this object
'action', // defines api endpoint used to submit this object to
'method', // defines http method to use on "action" endpoint
]
'schema', // url for a remote JSON Schema definition
];
/* The character that gets used to join multiple field values */
static joining_char = ',';
private _fields: Field[];
private _form: HTMLFormElement | null;
private _validate: ValidateFunction | undefined;
ready: Promise<void>;
private readyResolve!: () => void;
constructor() {
super();
@@ -109,6 +121,10 @@ export class Declaform extends HTMLElement {
this.shadowRoot!.innerHTML = '<slot name="form"></slot>';
this._fields = [];
this._form = null;
this._validate = undefined;
this.ready = new Promise(resolve => {
this.readyResolve = resolve;
})
}
async connectedCallback() {
@@ -151,10 +167,23 @@ export class Declaform extends HTMLElement {
this.hydrateFromEndpoint(src);
}
const schemaUrl = this.getAttribute('schema');
if (schemaUrl) {
const res = await fetch(schemaUrl);
const schema = await res.json();
this._validate = ajv.compile(schema);
}
this._form.addEventListener('submit', async (e) => {
e.preventDefault();
await this.submit();
})
const isValid = (this.hasAttribute('schema')) ?
await this.validate(e) : true;
if (isValid) {
await this.submit();
}
});
this.readyResolve();
}
get form() {
@@ -202,13 +231,24 @@ export class Declaform extends HTMLElement {
populateFields(obj, '');
}
async validate(e?: SubmitEvent) {
e?.preventDefault();
if (!this._validate) {
console.warn(`[Declaform] Warning: _validate is not defined`)
return true;
}
const obj = this.toObject();
const isValid = this._validate(obj);
return isValid;
}
private async hydrateFromEndpoint(endpoint: string): Promise<void> {
const response = await fetch(endpoint);
const obj = await response.json();
this.hydrate(obj);
}
private async submit(): Promise<void> {
async submit(): Promise<void> {
const method = this.getAttribute('method') ?? 'POST';
const endpoint = new URL(this.getAttribute('action') ?? '');
const obj = this.toObject();