-
Notifications
You must be signed in to change notification settings - Fork 40
Provided signature and partial type compatibility with InertiaJS (Clone of #65) #71
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Thanks, @juanparati. I don't want us to be replicating all of Inertia's types in Precognition. Ideally, we would just be extending their existing form to ensure compatibility. Do you know if this is currently possible? To my knowledge, Inertia does not currently export the types that we need to be able to extend. |
touched(name: string): boolean, // Patched | ||
touch(name: string | string[] | NamedInputEvent): this; // Patched | ||
submit(submitMethod: RequestMethod|Config, submitUrl?: string, submitOptions?: Partial<VisitOptions>): void; // Patched | ||
cancel(): void; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As an example, we shouldn't be specifying the cancel
function, as we don't provide this function. I would want this to come from Inertia's type definition that we extend.
@juanparati could you perhaps answer's Tim's questions when you find some time? |
@timacdonald : In fact I am currently extending the "useFormPrecognition" as a workaround. This is the code that I use: import type {RequestMethod, ValidationConfig} from 'laravel-precognition';
import {watch} from 'vue';
import {useForm} from 'laravel-precognition-vue-inertia';
import {debounce} from 'vue-debounce'
import deepEqual from "deep-equal";
export function useFormPrecognition<Data extends object>(
method: RequestMethod | (() => RequestMethod),
url: string | (() => string),
inputs: Data,
validationTrigger = {
autoValidate: true,
debounceTime: 800
},
config: ValidationConfig = {}
) {
// @ts-ignore
const form = useForm(method, url, inputs, config);
const validateChanges = (newVal: any, oldVal: any) => {
const changedFields: string[] = [];
for (const [k, v] of Object.entries(newVal)) {
const type = typeof v;
if (v === void 0)
continue;
if ('object' === type || 'symbol' === type || Array.isArray(v)) {
deepEqual(v, oldVal[k], {strict: true});
changedFields.push(k);
} else if (v !== oldVal[k]) {
changedFields.push(k);
}
}
changedFields.forEach(form.validate);
}
const debounceInstance = debounce(validateChanges, validationTrigger.debounceTime);
if (validationTrigger.autoValidate) {
watch(
() => form.data(),
(newVal, oldVal) => {
debounceInstance.cancel();
debounceInstance(newVal, oldVal);
},
{deep: true}
);
}
return form;
} |
A possible solution it's just to explain in the documentation how to extend "useForm". Or see #65 (comment) |
Could you elaborate on why it is not currently possible? |
It's not possible to extend directly "InertiaFormProps" from Inertia because it's not exported however I found a workaround that works ✨. It seems that I was enable to extend from InertiaForm type. I just tested and it works: import { Config, NamedInputEvent, RequestMethod, SimpleValidationErrors, toSimpleValidationErrors, ValidationConfig, ValidationErrors, resolveUrl, resolveMethod, Validator } from 'laravel-precognition'
import { useForm as usePrecognitiveForm, client } from 'laravel-precognition-vue'
import { InertiaForm, useForm as useInertiaForm } from '@inertiajs/vue3'
import { Progress } from '@inertiajs/core'
import { watchEffect } from 'vue'
export { client }
type FormDataType = object;
interface FormProps<TForm extends FormDataType> {
validating: boolean,
touched(name: string): boolean,
touch(name: string | string[] | NamedInputEvent): this;
progress: Progress | null;
valid(name: string | NamedInputEvent | string[]): boolean,
invalid(name: string): boolean,
clearErrors(...fields: (keyof TForm)[]) : this,
reset(...fields: (keyof TForm)[]) : void,
setErrors(errors: SimpleValidationErrors|ValidationErrors) : this;
forgetError(name: string|NamedInputEvent): this;
setError(field: keyof TForm, value: string): this;
transform(callback: (data: FormDataType) => Record<string, unknown>) : this;
validate(name?: string|NamedInputEvent): this;
setValidationTimeout(duration: number): this;
validateFiles(): this;
submit(submitMethod?: RequestMethod|Config, submitUrl?: string, submitOptions?: any): void
validator(): Validator;
}
export type PrecognitionFormProps<TForm extends FormDataType> = FormProps<TForm> & InertiaForm<TForm>
export const useForm = <Data extends FormDataType>(method: RequestMethod|(() => RequestMethod), url: string|(() => string), inputs: Data, config: ValidationConfig = {}): PrecognitionFormProps<Data> => {
/**
* The Inertia form.
*/
const inertiaForm = useInertiaForm(inputs)
/**
* The Precognitive form.
*/
const precognitiveForm = usePrecognitiveForm(method, url, inputs as Record<string, unknown>, config)
/**
* Setup event listeners.
*/
precognitiveForm.validator().on('errorsChanged', () => {
inertiaClearErrors()
inertiaSetError(
// @ts-expect-error
toSimpleValidationErrors(precognitiveForm.validator().errors())
)
})
/**
* The Inertia submit function.
*/
const inertiaSubmit = inertiaForm.submit.bind(inertiaForm)
/**
* The Inertia reset function.
*/
const inertiaReset = inertiaForm.reset.bind(inertiaForm)
/**
* The Inertia clear errors function.
*/
const inertiaClearErrors = inertiaForm.clearErrors.bind(inertiaForm)
/**
* The Inertia set error function.
*/
const inertiaSetError = inertiaForm.setError.bind(inertiaForm)
/**
* The Inertia trasform function.
*/
const inertiaTransform = inertiaForm.transform.bind(inertiaForm)
/**
* The transform function.
*/
let transformer: (data: Data) => FormDataType = (data) => data
/**
* Patch the form.
*/
const form = Object.assign(inertiaForm, {
validating: precognitiveForm.validating,
touched: precognitiveForm.touched,
touch(name: Array<string>|string|NamedInputEvent) {
precognitiveForm.touch(name)
return form
},
valid: precognitiveForm.valid,
invalid: precognitiveForm.invalid,
clearErrors(...fields: (keyof Data)[]) {
inertiaClearErrors(...fields)
if (fields.length === 0) {
precognitiveForm.setErrors({})
} else {
// @ts-expect-error
fields.forEach(precognitiveForm.forgetError)
}
return form
},
reset(...fields: (keyof Data)[]) {
inertiaReset(...fields)
precognitiveForm.reset(...fields as string[])
},
setErrors(errors: SimpleValidationErrors|ValidationErrors) {
precognitiveForm.setErrors(errors)
return form
},
forgetError(name: string|NamedInputEvent) {
precognitiveForm.forgetError(name)
return form
},
setError(key: any, value?: any) {
form.setErrors({
...inertiaForm.errors,
...typeof value === 'undefined'
? key
: { [key]: value },
})
return form
},
transform(callback: (data: Data) => Record<string, unknown>) {
inertiaTransform(callback)
transformer = callback
return form
},
validate(name?: string|NamedInputEvent) {
precognitiveForm.setData(inertiaForm.data() as Record<string, unknown>)
precognitiveForm.validate(name)
return form
},
setValidationTimeout(duration: number) {
precognitiveForm.setValidationTimeout(duration)
return form
},
validateFiles() {
precognitiveForm.validateFiles()
return form
},
submit(submitMethod: RequestMethod|Config = {}, submitUrl?: string, submitOptions?: any): void {
const isPatchedCall = typeof submitMethod !== 'string'
submitOptions = isPatchedCall
? submitMethod
: submitOptions
submitUrl = isPatchedCall
? resolveUrl(url)
: submitUrl!
submitMethod = isPatchedCall
? resolveMethod(method)
: submitMethod as RequestMethod
inertiaSubmit(submitMethod, submitUrl as string, {
...submitOptions,
onError: (errors: SimpleValidationErrors): any => {
precognitiveForm.validator().setErrors(errors)
if (submitOptions.onError) {
return submitOptions.onError(errors)
}
},
})
},
validator: precognitiveForm.validator,
})
// Due to the nature of `reactive` elements, reactivity is not inherited by
// the patched Inertia form as we have to destructure the Precog form. We
// can handle this by watching for changes and apply the changes manually.
watchEffect(() => form.validating = precognitiveForm.validating)
return form
} I was able to build without any error and I also did some small test with PHPStorm and it seems that it can provide all the hints from Precognition and InertiaJS (See the following example): Please let me know if this is a good solution so I can finish with some extra tests and then I will send you a PR. |
Nice. This looks like a good solution if it works. Thanks! |
@timacdonald : I created #77 |
This is a PR clone of #65.
It was closed because my old precognition repository was removed by mistake and the original PR was automatically closed.
Original description:
I did some rework on the vue-inertia package in order to make it compatible with the original InertiaJS "useForm":
Changes
import {useForm} from 'laravel-precognition-vue-inertia'
const form = useForm('post', 'https://example.net', mydata);
// or useForm('post', 'https://example.net', mydata as MyInterface);
Advantages of this change
Disadvantages of this change