![Oracle Drags Its Feet in the JavaScript Trademark Dispute](https://cdn.sanity.io/images/cgdhsj6q/production/919c3b22c24f93884c548d60cbb338e819ff2435-1024x1024.webp?w=400&fit=max&auto=format)
Security News
Oracle Drags Its Feet in the JavaScript Trademark Dispute
Oracle seeks to dismiss fraud claims in the JavaScript trademark dispute, delaying the case and avoiding questions about its right to the name.
reactive-form-model
Advanced tools
rxjs based, framework agnostic, tiny form state management library
This is a framework agnostic form model built on top of rxjs.
Features:
Will not be features:
type
or widget
field which indicates what input type this field is
as a state management lib, this lib does not care about such information, you should put this into metadata field, e.g.
import {FormControl} from "reactive-form-model"
import {of} from "rxjs"
const password = new FormControl("", {
metadata: of({
type: "password"
})
})
Framework support/Component implementation
Json-like schema form
This codesandbox shows how to use this lib with ant-design's form, form item and input components
basically, u define a model like this (example is in react):
import {of, fromFetch} from "rxjs"
import {publishReplay, refCount} from "rxjs/operators"
import {FormControl, FormControlList, FormControlGroup} from "reactive-form-model"
async function validatePhoneNumber(v){
return typeof v === 'number' || v.match(/^[0-9_+- ]+$/)
}
export function registrationFormModel(){
const username = new FormControl("")
const password = new FormControl("", {
metadata: of({
type: "password"
})
})
const passwordConfirm = new FormControl("", {
metadata: of({
type: "password"
})
})
const phoneNumbers = new FormControlList([], value=>{
return new FormControl(value, {
validator: validatePhoneNumber
})
})
const provinceList = fromFetch("/province").pipe(
publishReplay(1),
refCount()
)
const addresses = new FormControlList([] as {province: string, city: string}[], value=>{
const province = new FormControl(value.province, {
//provice a list for use when rendered, but we don't care what component is used in view layer.
metadata: provinceList
})
const city = new FormControl(value.city, {
//provice citylist
metadata: combineLatest([
province.value,
provinceList
]).pipe(
map(([selectedProvince, provinceList])=>{
const cityList = (selectedProvince && provinceList.find(x=>x.id === selectedProvince))?.cityList
return {
selectedProvince
cityList
}
})
)
})
//clear city after province changed
province.value.subscribe(()=>{
city.change("")
})
return new FormControlGroup({
province,
city,
})
})
return new FormControlGroup({
username,
password,
passwordConfirm,
phoneNumbers,
addresses
}, {
validator: async v=>{
if(!v.username){
return "must fill username"
}
if(v.password !== v.passwordConfirm){
return "password must be equal to passwordConfirm"
}
}
})
}
Then you create a connection component between the model and how to render then, this needs only done once and u can use in all forms.
If you are using ant-design, u can directly copy these.
import { Form, Col, Row, Button } from "antd"
import { FormItemProps } from "antd/lib/form"
import * as React from "react"
import { AbstractControl, ValidationInfo, FormControlList } from "reactive-form-model"
import { CloseOutlined, PlusOutlined, MinusOutlined } from "@ant-design/icons"
import { useSubscription } from "use-subscription"
type FormItemRenderChildren<T, Meta> = (inputProps: { value?: T; onChange?: (v: T) => void }, behavior: Meta | null, err: ValidationInfo) => React.ReactNode
export function FormItem<T, Meta>({
field,
children,
...rest
}: Omit<FormItemProps, "name" | "children"> & {
field: AbstractControl<T, Meta>
children: FormItemRenderChildren<T, Meta>
}) {
const value = useObservable(field.value)
const metadata = useObservable(field.metadata)
const error = useObservable(field.error)
return (
<Form.Item labelCol={{span:4}} wrapperCol={{span:20}} hasFeedback help={!!error ? String(error) : undefined} validateStatus={!!error ? "error" : undefined} {...rest}>
{children(
{
value: value === null ? undefined : value,
onChange: field.change,
},
meta,
error
)}
</Form.Item>
)
}
//eslint-disable-next-line
export function FormList<Meta, Children extends AbstractControl<any, any>>({
field,
children,
...rest
}: Omit<FormItemProps, "name" | "children"> & {
field: FormControlList<Meta, Children>
children: (child: Children, arrayMeta: Meta | null, index: number) => React.ReactNode
}) {
const items = useObservable(field.children)
const metadata = useObservable(field.metadata)
const error = useObservable(field.error)
return (
<Form.Item labelCol={{span:4}} wrapperCol={{span:20}} hasFeedback help={!!error ? String(error) : undefined} validateStatus={!!error ? "error" : undefined} {...rest}>
<Row>
<Col span={24}>
{items.map((x, i) => {
return (
<Row gutter={8} key={x.key}>
<Col>
<Button
icon={<MinusOutlined />}
onClick={() => {
field.delete(i)
}}
></Button>
</Col>
<Col span={22}>
{children(x.child, metadata, i)}
</Col>
</Row>
)
})}
</Col>
</Row>
<Row>
<Col span={24}>
<Button
icon={<PlusOutlined />}
onClick={() => {
//eslint-disable-next-line
field.push({} as any)
}}
></Button>
</Col>
</Row>
</Form.Item>
)
}
function useObservable<T>(ob: Observable<T>){
return useSubscription(useMemo(()=>{
let value = null
return {
getCurrentValue: ()=>value,
subscribe: cb=>{
const sub = ob.subscribe((v)=>{
value = v
cb()
})
return ()=>sub.unsubscribe()
}
}
}, [ob]))
}
Last u render it, this needs to be done for every single form.
function App(){
const form = useMemo(registrationFormModel,[])
const hasError = useObservable(form.error)
return <Form>
<FormItem label={"UserName"} field={form.children.username}>
{(props)=>{
return <Input {...props} maxLength={24}>
}}
</FormItem>
<FormItem label={"Password"} field={form.children.password}>
{(props)=>{
return <Input {...props} maxLength={24} type="password">
}}
</FormItem>
<FormItem label={"Confirm Password"} field={form.children.passwordConfirm}>
{(props)=>{
return <Input {...props} maxLength={24} type="password">
}}
</FormItem>
<FormList label="PhoneNumber" field={form.children.phoneNumbers}>
{field=>{
return <FormItem field={field}>
{(props)=>{
return <Input {...props} maxLength={24}>
}}
</FormItem>
}}
</FormList>
<FormList label="Addresses" field={form.children.addresses}>
{field=>{
return <>
<FormItem field={field.children.province}>
{(props, options)=>{
return <Select {...props} options={options}>
}}
</FormItem>
<FormItem field={field.children.city}>
{(props, options)=>{
return <Select {...props} options={options.cityList}>
}}
</FormItem>
</>
}}
</FormList>
<Button htmlType="submit" disabled={!!hasErro}>Submit</Button>
</Form>
}
FAQs
rxjs based, framework agnostic, tiny form state management library
We found that reactive-form-model demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Oracle seeks to dismiss fraud claims in the JavaScript trademark dispute, delaying the case and avoiding questions about its right to the name.
Security News
The Linux Foundation is warning open source developers that compliance with global sanctions is mandatory, highlighting legal risks and restrictions on contributions.
Security News
Maven Central now validates Sigstore signatures, making it easier for developers to verify the provenance of Java packages.