Solidjs를 사용한 멋진 양식
저는 Solidjs의 장점을 활용하고 사용자 입력 양식을 지원하기 위해 최소 9kb의 압축 라이브러리를 구축하기로 결정했습니다. rx-controls-solid . 우리가 무엇을 할 수 있는지 살펴보겠습니다(Solidjs에 대한 소개가 필요한 경우 참고).
typescript에서 간단한
TextField
구성 요소를 만들어 보겠습니다.import { withControl, FormControl } from 'rx-controls-solid';
export const TextField = withControl((props) => {
// prop.control is static for the lifetime of the component
const control = props.control as FormControl<string | null>;
return (
<label>
<span class='input-label'>{props.label}</span>
<input
type="text"
value={control.value}
oninput={(e) => {
control.markDirty(true);
control.setValue(e.currentTarget.value || null);
}}
onblur={() => control.markTouched(true)}
placeholder={props.placeholder}
/>
</label>
);
});
이 구성 요소는 사용자가 변경했는지
touched
(공지onblur
콜백) 사용자가 변경했는지(oninput
) 추적합니다. 사용자가 값을 변경하면 사용자가 값을 변경했는지 추적하기 위해 컨트롤을 dirty
로 표시합니다. 또한 입력과 자리 표시자에 레이블을 설정할 수 있습니다. 꽤 간단한 물건.그러나 텍스트 필드는 단독으로 거의 사용되지 않습니다. 주소 정보를 수집하는 구성 요소를 만들고 싶습니다. 여기에는
Street
, City
, State
및 Postcode
를 요청하는 작업이 포함됩니다. TextField
구성 요소를 사용하여 AddressForm
를 생성합니다.import { withControl, FormGroup, FormControl } from 'rx-controls-solid';
import { toSignal } from './utils';
const controlFactory = () =>
new FormGroup({
street: new FormControl<string | null>(null),
city: new FormControl<string | null>(null),
state: new FormControl<string | null>(null),
zip: new FormControl<string | null>(null),
});
export const AddressForm = withControl({
controlFactory,
component: (props) => {
const control = props.control;
const isControlValid = toSignal(control.observe('valid'));
const isControlTouched = toSignal(control.observe('touched'));
const isControlDirty = toSignal(control.observe('dirty'));
return (
<fieldset classList={{
"is-valid": isControlValid(),
"is-invalid": !isControlValid(),
"is-touched": isControlTouched(),
"is-untouched": !isControlTouched(),
"is-dirty": isControlDirty(),
"is-clean": !isControlDirty(),
}}>
<TextField label="Street" controlName="street" />
<TextField label="City" controlName="city" />
<TextField label="State" controlName="state" />
<TextField label="Postcode" controlName="zip" />
</fieldset>
);
},
});
주소 양식 자체도 래핑됩니다
withControl()
. 이렇게 하면 AddressForm
를 더 큰 상위 양식의 양식 구성 요소로도 사용할 수 있습니다.AddressForm
가 기본값FormGroup
이 아닌 FormControl
컨트롤을 사용하기를 원하므로 컨트롤을 초기화하는 controlFactory
함수를 제공합니다.const controlFactory = () =>
new FormGroup({
street: new FormControl<string | null>(null),
city: new FormControl<string | null>(null),
state: new FormControl<string | null>(null),
zip: new FormControl<string | null>(null),
});
export const AddressForm = withControl({
controlFactory,
component: (props) => {
const control = props.control;
const isControlValid = toSignal(control.observe('valid'));
// continued...
AddressForm 컨트롤을
TextField's
컨트롤에 연결하기 위해 해야 할 일은 controlName="street"
속성을 사용하여 부모의 어떤 FormControl이 자식 TextField와 연결되어야 하는지 지정하는 것이었습니다.<TextField label="Street" controlName="street" />
<TextField label="City" controlName="city" />
또한
AddressForm
가 유효한지/무효한지, 편집된/편집되지 않은지, 터치된/건드리지 않은지 여부에 따라 CSS 클래스를 적용하도록 구성 요소를 설정했습니다. 실제로 CSS 클래스를 쉽게 적용할 수 있는 도우미 기능이 있지만 교육을 위해 이 예제에서는 사용하지 않았습니다.AddressForm 구성 요소를 더 큰 양식에 연결하고 싶다고 가정해 보겠습니다. 그것도 쉽습니다!
// factory for initializing the `MyLargerForm` `FormGroup`
const controlFactory = () =>
new FormGroup({
firstName: new FormControl<string | null>(null),
address: new FormGroup({
street: new FormControl<string | null>(null),
city: new FormControl<string | null>(null),
state: new FormControl<string | null>(null),
zip: new FormControl<string | null>(null),
}),
});
// the form component itself
export const MyLargerForm = withControl({
controlFactory,
component: (props) => {
const control = props.control;
// because we can
const lastNameControl = new FormControl<string | null>(null);
return (
<form>
<fieldset>
<TextField label="First name" controlName="firstName" />
<TextField label="Last name" control={lastNameControl} />
</fieldset>
<AddressForm controlName="address" />
</form>
);
},
});
그리고 몇 단계만 거치면 매우 강력하고 매우 구성 가능한 양식 구성 요소 집합을 갖게 됩니다.
TextField
구성 요소에 변경 사항이 발생하면 해당 변경 사항이 위쪽으로 흐르고 상위FormGroup
구성 요소를 자동으로 업데이트합니다.이러한 변경 사항을 쉽게 듣고 부모를 통해 응답할 수 있습니다.
예를 들어, 양식의 어떤 부분이 터치될 때 수신 대기하려면
touched
속성 상태/변경 사항을 구독하면 됩니다.control.observe('touched').subscribe(v => {/* ... */})
특히 "firstName"컨트롤이 터치되었을 때 수신하려면
// this is similar to control.controls.firstName.touched
control.observe('controls', 'firstName', 'touched')
// or
control.get('firstName').observe('touched')
다음은 더 복잡한 고급 예입니다. 값 변경을 수신하고, 변경 비율을 디바운스하고, 유효성 검사를 수행하고, 유효성 검사가 완료될 때까지 기다리는 동안
control
를 보류 중으로 표시하려면 다음을 수행할 수 있습니다. firstName
컨트롤에 오류를 설정하면 "이름"TextField
이 잘못된 것으로 표시됩니다(점수!).import { interval } from 'rxjs';
import { switchMap, tap, take } from 'rxjs/operators';
import { myCustomValidationService } from './my-validation-service';
export const MyLargerForm = withControl({
// ...hiding the controlFactory boilerplate...
component: (props) => {
const control = props.control;
const firstName = control.get('firstName');
const sub = control.observe('value', 'firstName').pipe(
tap(() => firstName.markPending(true)),
switchMap(v => interval(500).pipe(
take(1),
switchMap(() => myCustomValidationService(v)),
tap(() => firstName.markPending(false)),
)),
).subscribe(result => {
if (result.errors) {
firstName.setErrors({ validationFailed: true });
} else {
firstName.setErrors(null);
}
});
const onsubmit (e) => {
e.preventDefault();
if (control.pending || control.invalid) return;
// do stuff...
};
onCleanup(() => sub.unsubscribe());
return (
<form onsubmit={onsubmit}>
<fieldset>
<TextField label="First name" controlName="firstName" />
<TextField label="Last name" control={lastNameControl} />
</fieldset>
<AddressForm controlName="address" />
</form>
);
},
});
이것은 실제로 할 수 있는 일의 표면을 긁는 것입니다
rx-controls-solid
. Check out the repo to read the documentation and learn more 또는 this codesandbox 을 사용하여 라이브러리를 가지고 놀 수 있습니다.리포지토리를 확인하세요
Reference
이 문제에 관하여(Solidjs를 사용한 멋진 양식), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/johncarroll/awesome-forms-with-solidjs-18gi텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)