Handle forms (no-JS)
This is a full example of how to handle forms in Gracile. Server-only handling, no JS needed.
π /src/routes/form.ts
import { defineRoutefunction defineRoute<GetHandlerData extends R.HandlerDataHtml = undefined, PostHandlerData extends R.HandlerDataHtml = undefined, CatchAllHandlerData extends R.HandlerDataHtml = undefined, StaticPathOptions extends R.StaticPathOptionsGeneric | undefined = undefined, RouteContext extends R.RouteContextGeneric = {
...;
}>(options: {
handler?: StaticPathOptions extends object ? never : R.Handler<CatchAllHandlerData> | {
GET?: R.Handler<GetHandlerData>;
POST?: R.Handler<PostHandlerData>;
QUERY?: R.Handler<Response>;
PUT?: R.Handler<Response>;
PATCH?: R.Handler<Response>;
DELETE?: R.Handler<Response>;
HEAD?: R.Handler<Response>;
OPTIONS?: R.Handler<Response>;
} | undefined;
staticPaths?: () => R.MaybePromise<StaticPathOptions[]> | undefined;
prerender?: boolean | undefined;
document?: R.DocumentTemplate<RouteContext> | undefined;
template?: R.BodyTemplate<RouteContext> | undefined;
}): (RouteModule: typeof R.RouteModule) => R.RouteModule
Defines a file-based route for Gracile to consume.
} from '@gracile/gracile/route';
import { htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>
Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html
tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
} from 'lit';
import { documentconst document: () => ServerRenderedTemplate
} from '../document.js';
// -----------------------------------------------------------------------------
let achievementsDblet achievementsDb: {
name: string;
coolnessFactor: number;
}[]
= [{ namename: string
: 'initial record', coolnessFactorcoolnessFactor: number
: 5 }];
Just a bit of optional setup, for easier refactoring and to keep a bird-eye view.
const FORMconst FORM: {
readonly fields: {
readonly action: "action";
readonly achievementName: "achievement_name";
readonly coolnessFactor: "coolness_factor";
readonly filterByName: "filter_by_name";
};
readonly actions: {
...;
};
}
= {
fieldsfields: {
readonly action: "action";
readonly achievementName: "achievement_name";
readonly coolnessFactor: "coolness_factor";
readonly filterByName: "filter_by_name";
}
: {
actionaction: "action"
: 'action',
achievementNameachievementName: "achievement_name"
: 'achievement_name',
coolnessFactorcoolnessFactor: "coolness_factor"
: 'coolness_factor',
filterByNamefilterByName: "filter_by_name"
: 'filter_by_name',
},
actionsactions: {
readonly deleteAll: "delete_all";
readonly add: "add";
}
: {
deleteAlldeleteAll: "delete_all"
: 'delete_all',
addadd: "add"
: 'add',
},
} as consttype const = {
readonly fields: {
readonly action: "action";
readonly achievementName: "achievement_name";
readonly coolnessFactor: "coolness_factor";
readonly filterByName: "filter_by_name";
};
readonly actions: {
...;
};
}
;
// -----------------------------------------------------------------------------
export default defineRoutedefineRoute<Response | {
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly filterByName: string | null;
}, Response | {
readonly success: false;
readonly message: "Wrong form input.";
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly payload: {
...;
};
} | {
...;
}, undefined, undefined, {
...;
}>(options: {
...;
}): (RouteModule: typeof RouteModule) => RouteModule
Defines a file-based route for Gracile to consume.
({
Order matters! Handlers return inferred props. for doc/page afterward.
handlerhandler?: Handler<undefined> | {
GET?: Handler<Response | {
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly filterByName: string | null;
}> | undefined;
... 6 more ...;
OPTIONS?: Handler<...> | undefined;
} | undefined
A function or an object containing functions named after HTTP methods.
A handler can return either a standard Response
that will terminate the
request pipeline, or any object to populate the current route template
and document contexts.
: {
GETGET?: Handler<Response | {
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly filterByName: string | null;
}> | undefined
: (contextcontext: {
url: URL;
params: Parameters;
request: Request;
locals: Gracile.Locals;
responseInit: ResponseInit;
}
) => {
const filterByNameconst filterByName: string | null
= contextcontext: {
url: URL;
params: Parameters;
request: Request;
locals: Gracile.Locals;
responseInit: ResponseInit;
}
.urlurl: URL
.searchParamsURL.searchParams: URLSearchParams
.getURLSearchParams.get(name: string): string | null
Returns the first value associated to the given search parameter.
(
FORMconst FORM: {
readonly fields: {
readonly action: "action";
readonly achievementName: "achievement_name";
readonly coolnessFactor: "coolness_factor";
readonly filterByName: "filter_by_name";
};
readonly actions: {
...;
};
}
.fieldsfields: {
readonly action: "action";
readonly achievementName: "achievement_name";
readonly coolnessFactor: "coolness_factor";
readonly filterByName: "filter_by_name";
}
.filterByNamefilterByName: "filter_by_name"
,
);
if (filterByNameconst filterByName: string | null
) {
const filteredconst filtered: {
name: string;
coolnessFactor: number;
}[]
= achievementsDblet achievementsDb: {
name: string;
coolnessFactor: number;
}[]
.filterArray<{ name: string; coolnessFactor: number; }>.filter(predicate: (value: {
name: string;
coolnessFactor: number;
}, index: number, array: {
name: string;
coolnessFactor: number;
}[]) => unknown, thisArg?: any): {
name: string;
coolnessFactor: number;
}[] (+1 overload)
Returns the elements of an array that meet the condition specified in a callback function.
((achievementachievement: {
name: string;
coolnessFactor: number;
}
) =>
achievementachievement: {
name: string;
coolnessFactor: number;
}
.namename: string
.includesString.includes(searchString: string, position?: number): boolean
Returns true if searchString appears as a substring of the result of converting this
object to a String, at one or more positions that are
greater than or equal to position; otherwise, returns false.
(filterByNameconst filterByName: string
),
);
return { achievementsachievements: {
name: string;
coolnessFactor: number;
}[]
: filteredconst filtered: {
name: string;
coolnessFactor: number;
}[]
, filterByNamefilterByName: string | null
} as consttype const = {
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly filterByName: string;
}
;
}
Clean-up empty search parameter
if (filterByNameconst filterByName: string | null
=== '') {
contextcontext: {
url: URL;
params: Parameters;
request: Request;
locals: Gracile.Locals;
responseInit: ResponseInit;
}
.urlurl: URL
.searchParamsURL.searchParams: URLSearchParams
.deleteURLSearchParams.delete(name: string, value?: string): void
Deletes the given search parameter, and its associated value, from the list of all search parameters.
(FORMconst FORM: {
readonly fields: {
readonly action: "action";
readonly achievementName: "achievement_name";
readonly coolnessFactor: "coolness_factor";
readonly filterByName: "filter_by_name";
};
readonly actions: {
...;
};
}
.fieldsfields: {
readonly action: "action";
readonly achievementName: "achievement_name";
readonly coolnessFactor: "coolness_factor";
readonly filterByName: "filter_by_name";
}
.filterByNamefilterByName: "filter_by_name"
);
return Responsevar Response: {
new (body?: BodyInit | null, init?: ResponseInit): Response;
prototype: Response;
error(): Response;
json(data: any, init?: ResponseInit): Response;
redirect(url: string | URL, status?: number): Response;
}
This Fetch API interface represents the response to a request.
.redirectfunction redirect(url: string | URL, status?: number): Response
(contextcontext: {
url: URL;
params: Parameters;
request: Request;
locals: Gracile.Locals;
responseInit: ResponseInit;
}
.urlurl: URL
);
}
return { achievementsachievements: {
name: string;
coolnessFactor: number;
}[]
: achievementsDblet achievementsDb: {
name: string;
coolnessFactor: number;
}[]
, filterByNamefilterByName: string | null
} as consttype const = {
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly filterByName: string | null;
}
;
},
POSTPOST?: Handler<Response | {
readonly success: false;
readonly message: "Wrong form input.";
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly payload: {
readonly name: string | undefined;
readonly coolnessFactor: string | undefined;
};
} | {
...;
}> | undefined
: async (contextcontext: {
url: URL;
params: Parameters;
request: Request;
locals: Gracile.Locals;
responseInit: ResponseInit;
}
) => {
const formDataconst formData: FormData
= await contextcontext: {
url: URL;
params: Parameters;
request: Request;
locals: Gracile.Locals;
responseInit: ResponseInit;
}
.requestrequest: Request
.formDataBody.formData(): Promise<FormData>
();
const actionconst action: string | undefined
= formDataconst formData: FormData
.getFormData.get(name: string): FormDataEntryValue | null
(FORMconst FORM: {
readonly fields: {
readonly action: "action";
readonly achievementName: "achievement_name";
readonly coolnessFactor: "coolness_factor";
readonly filterByName: "filter_by_name";
};
readonly actions: {
...;
};
}
.fieldsfields: {
readonly action: "action";
readonly achievementName: "achievement_name";
readonly coolnessFactor: "coolness_factor";
readonly filterByName: "filter_by_name";
}
.actionaction: "action"
)?.toStringfunction toString(): string
Returns a string representation of a string.
();
switch (actionconst action: string | undefined
) {
case FORMconst FORM: {
readonly fields: {
readonly action: "action";
readonly achievementName: "achievement_name";
readonly coolnessFactor: "coolness_factor";
readonly filterByName: "filter_by_name";
};
readonly actions: {
...;
};
}
.actionsactions: {
readonly deleteAll: "delete_all";
readonly add: "add";
}
.addadd: "add"
:
{
const nameconst name: string | undefined
= formDataconst formData: FormData
.getFormData.get(name: string): FormDataEntryValue | null
(FORMconst FORM: {
readonly fields: {
readonly action: "action";
readonly achievementName: "achievement_name";
readonly coolnessFactor: "coolness_factor";
readonly filterByName: "filter_by_name";
};
readonly actions: {
...;
};
}
.fieldsfields: {
readonly action: "action";
readonly achievementName: "achievement_name";
readonly coolnessFactor: "coolness_factor";
readonly filterByName: "filter_by_name";
}
.achievementNameachievementName: "achievement_name"
)?.toStringfunction toString(): string
Returns a string representation of a string.
();
const coolnessFactorconst coolnessFactor: string | undefined
= formDataconst formData: FormData
.getFormData.get(name: string): FormDataEntryValue | null
(FORMconst FORM: {
readonly fields: {
readonly action: "action";
readonly achievementName: "achievement_name";
readonly coolnessFactor: "coolness_factor";
readonly filterByName: "filter_by_name";
};
readonly actions: {
...;
};
}
.fieldsfields: {
readonly action: "action";
readonly achievementName: "achievement_name";
readonly coolnessFactor: "coolness_factor";
readonly filterByName: "filter_by_name";
}
.coolnessFactorcoolnessFactor: "coolness_factor"
)
?.toStringfunction toString(): string
Returns a string representation of a string.
();
Basic form data shape validation.
if (nameconst name: string | undefined
&& coolnessFactorconst coolnessFactor: string | undefined
&& nameconst name: string
.lengthString.length: number
Returns the length of a String object.
>= 3) {
achievementsDblet achievementsDb: {
name: string;
coolnessFactor: number;
}[]
.pushArray<{ name: string; coolnessFactor: number; }>.push(...items: {
name: string;
coolnessFactor: number;
}[]): number
Appends new elements to the end of an array, and returns the new length of the array.
({
namename: string
,
coolnessFactorcoolnessFactor: number
: Numbervar Number: NumberConstructor
(value?: any) => number
An object that represents a number of any kind. All JavaScript numbers are 64-bit floating-point numbers.
(coolnessFactorconst coolnessFactor: string
),
});
} else {
contextcontext: {
url: URL;
params: Parameters;
request: Request;
locals: Gracile.Locals;
responseInit: ResponseInit;
}
.responseInitresponseInit: ResponseInit
Let you mutate the downstream page response.
It doesn't take effect if you're returning the
response yourself before (within your request handler).
.statusResponseInit.status?: number | undefined
= 400;
We want the user data to be repopulated in the page after a failed `POST`.
return {
successsuccess: false
: false,
messagemessage: "Wrong form input."
: 'Wrong form input.',
achievementsachievements: {
name: string;
coolnessFactor: number;
}[]
: achievementsDblet achievementsDb: {
name: string;
coolnessFactor: number;
}[]
,
payloadpayload: {
readonly name: string | undefined;
readonly coolnessFactor: string | undefined;
}
: { namename: string | undefined
, coolnessFactorcoolnessFactor: string | undefined
},
} as consttype const = {
readonly success: false;
readonly message: "Wrong form input.";
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly payload: {
readonly name: string | undefined;
readonly coolnessFactor: string | undefined;
};
}
;
}
}
break;
case FORMconst FORM: {
readonly fields: {
readonly action: "action";
readonly achievementName: "achievement_name";
readonly coolnessFactor: "coolness_factor";
readonly filterByName: "filter_by_name";
};
readonly actions: {
...;
};
}
.actionsactions: {
readonly deleteAll: "delete_all";
readonly add: "add";
}
.deleteAlldeleteAll: "delete_all"
:
achievementsDblet achievementsDb: {
name: string;
coolnessFactor: number;
}[]
= [];
break;
default:
contextcontext: {
url: URL;
params: Parameters;
request: Request;
locals: Gracile.Locals;
responseInit: ResponseInit;
}
.responseInitresponseInit: ResponseInit
Let you mutate the downstream page response.
It doesn't take effect if you're returning the
response yourself before (within your request handler).
.statusResponseInit.status?: number | undefined
= 422;
return {
successsuccess: false
: false,
messagemessage: "Unknown form action."
: 'Unknown form action.',
achievementsachievements: {
name: string;
coolnessFactor: number;
}[]
: achievementsDblet achievementsDb: {
name: string;
coolnessFactor: number;
}[]
,
} as consttype const = {
readonly success: false;
readonly message: "Unknown form action.";
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
}
;
}
Using the "POST/Redirect/GET" pattern to avoid duplicate form submissions
return Responsevar Response: {
new (body?: BodyInit | null, init?: ResponseInit): Response;
prototype: Response;
error(): Response;
json(data: any, init?: ResponseInit): Response;
redirect(url: string | URL, status?: number): Response;
}
This Fetch API interface represents the response to a request.
.redirectfunction redirect(url: string | URL, status?: number): Response
(contextcontext: {
url: URL;
params: Parameters;
request: Request;
locals: Gracile.Locals;
responseInit: ResponseInit;
}
.urlurl: URL
, 303);
},
},
documentdocument?: DocumentTemplate<{
url: URL;
props: {
GET: {
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly filterByName: string | null;
};
POST: {
readonly success: false;
readonly message: "Wrong form input.";
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly payload: {
readonly name: string | undefined;
readonly coolnessFactor: string | undefined;
};
} | {
...;
};
};
params: Parameters;
}> | undefined
A function that returns a server only template.
Route context is provided at runtime during the build.
: (contextcontext: {
url: URL;
props: {
GET: {
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly filterByName: string | null;
};
POST: {
readonly success: false;
readonly message: "Wrong form input.";
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly payload: {
readonly name: string | undefined;
readonly coolnessFactor: string | undefined;
};
} | {
...;
};
};
params: Parameters;
}
) => documentfunction document(): ServerRenderedTemplate
(contextcontext: {
url: URL;
props: {
GET: {
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly filterByName: string | null;
};
POST: {
readonly success: false;
readonly message: "Wrong form input.";
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly payload: {
readonly name: string | undefined;
readonly coolnessFactor: string | undefined;
};
} | {
...;
};
};
params: Parameters;
}
),
templatetemplate?: BodyTemplate<{
url: URL;
props: {
GET: {
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly filterByName: string | null;
};
POST: {
readonly success: false;
readonly message: "Wrong form input.";
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly payload: {
readonly name: string | undefined;
readonly coolnessFactor: string | undefined;
};
} | {
...;
};
};
params: Parameters;
}> | undefined
A function that returns a server only or a Lit client hydratable template.
Route context is provided at runtime during the build.
: (contextcontext: {
url: URL;
props: {
GET: {
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly filterByName: string | null;
};
POST: {
readonly success: false;
readonly message: "Wrong form input.";
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly payload: {
readonly name: string | undefined;
readonly coolnessFactor: string | undefined;
};
} | {
...;
};
};
params: Parameters;
}
) => htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>
Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html
tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
`
<h1>Achievements manager</h1>
<form method="post">
<input
type="hidden"
name=${FORMconst FORM: {
readonly fields: {
readonly action: "action";
readonly achievementName: "achievement_name";
readonly coolnessFactor: "coolness_factor";
readonly filterByName: "filter_by_name";
};
readonly actions: {
...;
};
}
.fieldsfields: {
readonly action: "action";
readonly achievementName: "achievement_name";
readonly coolnessFactor: "coolness_factor";
readonly filterByName: "filter_by_name";
}
.actionaction: "action"
}
value=${FORMconst FORM: {
readonly fields: {
readonly action: "action";
readonly achievementName: "achievement_name";
readonly coolnessFactor: "coolness_factor";
readonly filterByName: "filter_by_name";
};
readonly actions: {
...;
};
}
.actionsactions: {
readonly deleteAll: "delete_all";
readonly add: "add";
}
.addadd: "add"
}
/>
<input
type="text"
name=${FORMconst FORM: {
readonly fields: {
readonly action: "action";
readonly achievementName: "achievement_name";
readonly coolnessFactor: "coolness_factor";
readonly filterByName: "filter_by_name";
};
readonly actions: {
...;
};
}
.fieldsfields: {
readonly action: "action";
readonly achievementName: "achievement_name";
readonly coolnessFactor: "coolness_factor";
readonly filterByName: "filter_by_name";
}
.achievementNameachievementName: "achievement_name"
}
value=${contextcontext: {
url: URL;
props: {
GET: {
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly filterByName: string | null;
};
POST: {
readonly success: false;
readonly message: "Wrong form input.";
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly payload: {
readonly name: string | undefined;
readonly coolnessFactor: string | undefined;
};
} | {
...;
};
};
params: Parameters;
}
.propsprops: {
GET: {
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly filterByName: string | null;
};
POST: {
readonly success: false;
readonly message: "Wrong form input.";
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly payload: {
readonly name: string | undefined;
readonly coolnessFactor: string | undefined;
};
} | {
...;
};
}
.POSTtype POST: {
readonly success: false;
readonly message: "Wrong form input.";
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly payload: {
readonly name: string | undefined;
readonly coolnessFactor: string | undefined;
};
} | {
...;
}
?.payloadpayload?: {
readonly name: string | undefined;
readonly coolnessFactor: string | undefined;
} | undefined
?.namename: string | undefined
?? ''}
required
/>
<input
type="number"
value=${contextcontext: {
url: URL;
props: {
GET: {
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly filterByName: string | null;
};
POST: {
readonly success: false;
readonly message: "Wrong form input.";
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly payload: {
readonly name: string | undefined;
readonly coolnessFactor: string | undefined;
};
} | {
...;
};
};
params: Parameters;
}
.propsprops: {
GET: {
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly filterByName: string | null;
};
POST: {
readonly success: false;
readonly message: "Wrong form input.";
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly payload: {
readonly name: string | undefined;
readonly coolnessFactor: string | undefined;
};
} | {
...;
};
}
.POSTtype POST: {
readonly success: false;
readonly message: "Wrong form input.";
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly payload: {
readonly name: string | undefined;
readonly coolnessFactor: string | undefined;
};
} | {
...;
}
?.payloadpayload?: {
readonly name: string | undefined;
readonly coolnessFactor: string | undefined;
} | undefined
?.coolnessFactorcoolnessFactor: string | undefined
?? 1}
name=${FORMconst FORM: {
readonly fields: {
readonly action: "action";
readonly achievementName: "achievement_name";
readonly coolnessFactor: "coolness_factor";
readonly filterByName: "filter_by_name";
};
readonly actions: {
...;
};
}
.fieldsfields: {
readonly action: "action";
readonly achievementName: "achievement_name";
readonly coolnessFactor: "coolness_factor";
readonly filterByName: "filter_by_name";
}
.coolnessFactorcoolnessFactor: "coolness_factor"
}
/>
<footer>
<button>Add an achievement</button>
${contextcontext: {
url: URL;
props: {
GET: {
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly filterByName: string | null;
};
POST: {
readonly success: false;
readonly message: "Wrong form input.";
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly payload: {
readonly name: string | undefined;
readonly coolnessFactor: string | undefined;
};
} | {
...;
};
};
params: Parameters;
}
.propsprops: {
GET: {
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly filterByName: string | null;
};
POST: {
readonly success: false;
readonly message: "Wrong form input.";
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly payload: {
readonly name: string | undefined;
readonly coolnessFactor: string | undefined;
};
} | {
...;
};
}
.POSTtype POST: {
readonly success: false;
readonly message: "Wrong form input.";
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly payload: {
readonly name: string | undefined;
readonly coolnessFactor: string | undefined;
};
} | {
...;
}
?.successsuccess: false
=== false
? htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>
Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html
tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
`
<strong>Something went wrong!</strong>
${contextcontext: {
url: URL;
props: {
GET: {
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly filterByName: string | null;
};
POST: {
readonly success: false;
readonly message: "Wrong form input.";
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly payload: {
readonly name: string | undefined;
readonly coolnessFactor: string | undefined;
};
} | {
...;
};
};
params: Parameters;
}
.propsprops: {
GET: {
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly filterByName: string | null;
};
POST: {
readonly success: false;
readonly message: "Wrong form input.";
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly payload: {
readonly name: string | undefined;
readonly coolnessFactor: string | undefined;
};
} | {
...;
};
}
.POSTtype POST: {
readonly success: false;
readonly message: "Wrong form input.";
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly payload: {
readonly name: string | undefined;
readonly coolnessFactor: string | undefined;
};
} | {
...;
}
?.messagemessage: "Wrong form input." | "Unknown form action."
? htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>
Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html
tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
` <strong>${contextcontext: {
url: URL;
props: {
GET: {
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly filterByName: string | null;
};
POST: {
readonly success: false;
readonly message: "Wrong form input.";
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly payload: {
readonly name: string | undefined;
readonly coolnessFactor: string | undefined;
};
} | {
...;
};
};
params: Parameters;
}
.propsprops: {
GET: {
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly filterByName: string | null;
};
POST: {
readonly success: false;
readonly message: "Wrong form input.";
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly payload: {
readonly name: string | undefined;
readonly coolnessFactor: string | undefined;
};
} | {
...;
};
}
.POSTtype POST: {
readonly success: false;
readonly message: "Wrong form input.";
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly payload: {
readonly name: string | undefined;
readonly coolnessFactor: string | undefined;
};
} | {
...;
}
.messagemessage: "Wrong form input." | "Unknown form action."
}</strong> `
: null}
`
: null}
</footer>
</form>
<hr />
<form>
<input
type="text"
name=${FORMconst FORM: {
readonly fields: {
readonly action: "action";
readonly achievementName: "achievement_name";
readonly coolnessFactor: "coolness_factor";
readonly filterByName: "filter_by_name";
};
readonly actions: {
...;
};
}
.fieldsfields: {
readonly action: "action";
readonly achievementName: "achievement_name";
readonly coolnessFactor: "coolness_factor";
readonly filterByName: "filter_by_name";
}
.filterByNamefilterByName: "filter_by_name"
}
value=${contextcontext: {
url: URL;
props: {
GET: {
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly filterByName: string | null;
};
POST: {
readonly success: false;
readonly message: "Wrong form input.";
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly payload: {
readonly name: string | undefined;
readonly coolnessFactor: string | undefined;
};
} | {
...;
};
};
params: Parameters;
}
.propsprops: {
GET: {
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly filterByName: string | null;
};
POST: {
readonly success: false;
readonly message: "Wrong form input.";
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly payload: {
readonly name: string | undefined;
readonly coolnessFactor: string | undefined;
};
} | {
...;
};
}
.GETtype GET: {
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly filterByName: string | null;
}
?.filterByNamefilterByName: string | null
?? ''}
/>
<button>Filter by name</button>
</form>
<ul>
${(contextcontext: {
url: URL;
props: {
GET: {
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly filterByName: string | null;
};
POST: {
readonly success: false;
readonly message: "Wrong form input.";
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly payload: {
readonly name: string | undefined;
readonly coolnessFactor: string | undefined;
};
} | {
...;
};
};
params: Parameters;
}
.propsprops: {
GET: {
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly filterByName: string | null;
};
POST: {
readonly success: false;
readonly message: "Wrong form input.";
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly payload: {
readonly name: string | undefined;
readonly coolnessFactor: string | undefined;
};
} | {
...;
};
}
.GETtype GET: {
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly filterByName: string | null;
}
|| contextcontext: {
url: URL;
props: {
GET: {
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly filterByName: string | null;
};
POST: {
readonly success: false;
readonly message: "Wrong form input.";
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly payload: {
readonly name: string | undefined;
readonly coolnessFactor: string | undefined;
};
} | {
...;
};
};
params: Parameters;
}
.propsprops: {
GET: {
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly filterByName: string | null;
};
POST: {
readonly success: false;
readonly message: "Wrong form input.";
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly payload: {
readonly name: string | undefined;
readonly coolnessFactor: string | undefined;
};
} | {
...;
};
}
.POSTtype POST: {
readonly success: false;
readonly message: "Wrong form input.";
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly payload: {
readonly name: string | undefined;
readonly coolnessFactor: string | undefined;
};
} | {
...;
}
)?.achievementsachievements: {
name: string;
coolnessFactor: number;
}[]
?.mapArray<{ name: string; coolnessFactor: number; }>.map<TemplateResult<1>>(callbackfn: (value: {
name: string;
coolnessFactor: number;
}, index: number, array: {
name: string;
coolnessFactor: number;
}[]) => TemplateResult<1>, thisArg?: any): TemplateResult<...>[]
Calls a defined callback function on each element of an array, and returns an array that contains the results.
(
(achievementachievement: {
name: string;
coolnessFactor: number;
}
) => htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>
Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html
tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
`
<li>
<!-- -->
<em>${achievementachievement: {
name: string;
coolnessFactor: number;
}
.coolnessFactorcoolnessFactor: number
}</em> -
<strong>${achievementachievement: {
name: string;
coolnessFactor: number;
}
.namename: string
}</strong>
</li>
`,
)}
</ul>
<hr />
<form method="post">
<input
type="hidden"
name=${FORMconst FORM: {
readonly fields: {
readonly action: "action";
readonly achievementName: "achievement_name";
readonly coolnessFactor: "coolness_factor";
readonly filterByName: "filter_by_name";
};
readonly actions: {
...;
};
}
.fieldsfields: {
readonly action: "action";
readonly achievementName: "achievement_name";
readonly coolnessFactor: "coolness_factor";
readonly filterByName: "filter_by_name";
}
.actionaction: "action"
}
value=${FORMconst FORM: {
readonly fields: {
readonly action: "action";
readonly achievementName: "achievement_name";
readonly coolnessFactor: "coolness_factor";
readonly filterByName: "filter_by_name";
};
readonly actions: {
...;
};
}
.actionsactions: {
readonly deleteAll: "delete_all";
readonly add: "add";
}
.deleteAlldeleteAll: "delete_all"
}
/>
<button>Delete all</button>
</form>
<hr />
<footer>
<pre>${JSONvar JSON: JSON
An intrinsic object that provides functions to convert JavaScript values to and from the JavaScript Object Notation (JSON) format.
.stringifyJSON.stringify(value: any, replacer?: (number | string)[] | null, space?: string | number): string (+1 overload)
Converts a JavaScript value to a JavaScript Object Notation (JSON) string.
({ propsprops: {
GET: {
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly filterByName: string | null;
};
POST: {
readonly success: false;
readonly message: "Wrong form input.";
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly payload: {
readonly name: string | undefined;
readonly coolnessFactor: string | undefined;
};
} | {
...;
};
}
: contextcontext: {
url: URL;
props: {
GET: {
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly filterByName: string | null;
};
POST: {
readonly success: false;
readonly message: "Wrong form input.";
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly payload: {
readonly name: string | undefined;
readonly coolnessFactor: string | undefined;
};
} | {
...;
};
};
params: Parameters;
}
.propsprops: {
GET: {
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly filterByName: string | null;
};
POST: {
readonly success: false;
readonly message: "Wrong form input.";
readonly achievements: {
name: string;
coolnessFactor: number;
}[];
readonly payload: {
readonly name: string | undefined;
readonly coolnessFactor: string | undefined;
};
} | {
...;
};
}
}, null, 2)}</pre>
</footer>
`,
});