laravel-jetstream
Version:
<p align="center"><img src="https://laravel.com/assets/img/components/logo-jetstream.svg"></p>
302 lines (239 loc) • 7.96 kB
JavaScript
import { Inertia } from '@inertiajs/inertia'
import { shallowReactive } from 'vue'
import {
guardAgainstReservedFieldName,
isArray,
isFile,
merge,
objectToFormData,
reservedFieldNames
} from './util';
class InertiaForm {
constructor(data = {}, options = {}) {
this.processing = false;
this.successful = false;
this.recentlySuccessful = false;
this.isDirty = false;
this.withData(data)
.withOptions(options)
return new Proxy(this, {
set(obj, prop, value) {
obj[prop] = value;
if ((reservedFieldNames.indexOf(prop) === -1) && value !== obj.initial[prop]) {
obj.isDirty = true;
}
return true;
}
})
}
static create(data = {}) {
return new InertiaForm().withData(data);
}
withData(data) {
if (isArray(data)) {
data = data.reduce((carry, element) => {
carry[element] = '';
return carry;
}, {});
}
this.setInitialValues(data);
this.processing = false;
this.successful = false;
for (const field in data) {
guardAgainstReservedFieldName(field);
this[field] = data[field];
}
this.isDirty = false;
return this;
}
withOptions(options) {
this.__options = {
bag: 'default',
resetOnSuccess: true,
setInitialOnSuccess: false,
};
if (options.hasOwnProperty('bag')) {
this.__options.bag = options.bag;
}
if (options.hasOwnProperty('resetOnSuccess')) {
this.__options.resetOnSuccess = options.resetOnSuccess;
}
if (options.hasOwnProperty('setInitialOnSuccess')) {
this.__options.setInitialOnSuccess = options.setInitialOnSuccess;
}
return this;
}
withInertia(inertia) {
this.__inertia = inertia;
return this;
}
withPage(page) {
this.__page = page;
return this;
}
data() {
const data = {
'_error_bag': this.__options.bag
};
for (const property in this.initial) {
data[property] = this[property];
}
return data;
}
reset() {
merge(this, this.initial);
this.isDirty = false;
}
setInitialValues(values) {
this.initial = {};
merge(this.initial, values);
}
post(url, options = {}) {
return this.submit('post', url, options);
}
put(url, options = {}) {
return this.submit('put', url, options);
}
patch(url, options = {}) {
return this.submit('patch', url, options);
}
delete(url, options) {
return this.submit('delete', url, options);
}
submit(requestType, url, options) {
this.__validateRequestType(requestType);
this.processing = true;
this.successful = false;
const onSuccess = page => {
this.processing = false;
if (! this.hasErrors()) {
this.onSuccess();
} else {
this.onFail();
}
if (options.onSuccess) {
options.onSuccess(page)
}
}
if (requestType === 'delete') {
return this.__inertia[requestType](url, { ... options, onSuccess })
}
return this.__inertia[requestType](url, this.hasFiles() ? objectToFormData(this.data()) : this.data(), { ... options, onSuccess })
}
hasFiles() {
for (const property in this.initial) {
if (this.hasFilesDeep(this[property])) {
return true;
}
}
return false;
};
hasFilesDeep(object) {
if (object === null) {
return false;
}
if (typeof object === 'object') {
for (const key in object) {
if (object.hasOwnProperty(key)) {
if (this.hasFilesDeep(object[key])) {
return true;
}
}
}
}
if (Array.isArray(object)) {
for (const key in object) {
if (object.hasOwnProperty(key)) {
return this.hasFilesDeep(object[key]);
}
}
}
return isFile(object);
}
onSuccess() {
this.successful = true;
this.recentlySuccessful = true;
setTimeout(() => this.recentlySuccessful = false, 2000);
if (this.__options.resetOnSuccess) {
this.reset();
} else if (this.__options.setInitialOnSuccess) {
const { _error_bag, ...data } = this.data();
this.setInitialValues(data);
this.isDirty = false;
}
}
onFail() {
this.successful = false;
this.recentlySuccessful = false;
}
hasErrors() {
return this.inertiaPage().errorBags[this.__options.bag] &&
Object.keys(this.inertiaPage().errorBags[this.__options.bag]).length > 0;
}
hasError(field) {
return this.hasErrors() &&
this.inertiaPage().errorBags[this.__options.bag][field] &&
this.inertiaPage().errorBags[this.__options.bag][field].length > 0;
}
error(field) {
if (this.hasError(field)) {
return this.inertiaPage().errorBags[this.__options.bag][field][0];
}
}
errors(field = null) {
if (field === null) {
return this.hasErrors()
? Object.values(this.inertiaPage().errorBags[this.__options.bag])
.reduce((a, b) => a.concat(b), [])
: [];
}
return this.hasError(field)
? this.inertiaPage().errorBags[this.__options.bag][field]
: [];
}
inertiaPage() {
return this.__page()
}
__validateRequestType(requestType) {
const requestTypes = ['get', 'post', 'put', 'patch', 'delete'];
if (requestTypes.indexOf(requestType) === -1) {
throw new Error(
`\`${requestType}\` is not a valid request type, ` +
`must be one of: \`${requestTypes.join('`, `')}\`.`
);
}
}
}
export function useForm(data, options) {
const adapter = require('@inertiajs/inertia-vue3')
if (! adapter || ! shallowReactive) throw Error('The useForm hook may only be used with Vue 3 and @inertiajs/inertia-vue3.')
return shallowReactive(
InertiaForm.create()
.withData(data)
.withOptions(options)
.withInertia(Inertia)
.withPage(() => adapter.usePage().props.value)
)
}
export default {
install(app) {
if (app.version.split('.')[0] == 3) {
Object.defineProperty(app.config.globalProperties.$inertia, 'form', {
value: (data = {}, options = {}) =>
InertiaForm.create()
.withData(data)
.withOptions(options)
.withInertia(app.config.globalProperties.$inertia)
.withPage(() => app.config.globalProperties.$page.hasOwnProperty('props') ? app.config.globalProperties.$page.props : app.config.globalProperties.$page),
});
} else {
app.prototype.$inertia.form = (data = {}, options = {}) => {
return InertiaForm.create()
.withData(data)
.withOptions(options)
.withInertia(app.prototype.$inertia)
.withPage(() => app.prototype.$page.hasOwnProperty('props') ? app.prototype.$page.props : app.prototype.$page);
};
}
},
};