Files
mixtape/zero.Web/App/Components/Forms/form.vue
T

183 lines
3.9 KiB
Vue
Raw Normal View History

2020-04-08 16:14:12 +02:00
<template>
2020-04-10 01:17:16 +02:00
<form class="ui-form" @submit.prevent="onSubmit" @change="onChange">
2020-04-10 11:12:11 +02:00
<slot v-bind="slotProps" />
2020-04-08 16:14:12 +02:00
</form>
</template>
<script>
2020-04-08 23:33:56 +02:00
import Overlay from 'zeroservices/overlay.js'
import { isArray as _isArray, filter as _filter, groupBy as _groupBy } from 'underscore'
2020-04-08 23:33:56 +02:00
2020-04-08 16:14:12 +02:00
export default {
name: 'uiForm',
2020-04-09 15:53:03 +02:00
props: {
submit: {
type: Function,
default: () => { }
},
2020-04-10 01:17:16 +02:00
mapState: {
type: Boolean,
default: true
2020-04-10 11:12:11 +02:00
},
errorComponents: {
type: Array,
default: () => [ 'uiError' ]
2020-04-09 15:53:03 +02:00
}
},
2020-04-08 16:14:12 +02:00
data: () => ({
dirty: false,
2020-04-10 11:12:11 +02:00
state: 'default',
errors: [],
2020-04-10 11:12:11 +02:00
slotProps: {
state: null
}
2020-04-08 16:14:12 +02:00
}),
2020-04-10 11:12:11 +02:00
watch: {
state(val)
{
this.slotProps.state = val;
}
},
2020-04-08 16:14:12 +02:00
created()
{
2020-04-10 11:12:11 +02:00
this.slotProps.state = this.state;
2020-04-08 16:14:12 +02:00
},
methods: {
2020-04-09 15:53:03 +02:00
// shows a confirmation dialog for dirty forms when the route tries to change
// it only works when this method is attached to the route component
2020-04-08 23:33:56 +02:00
beforeRouteLeave(to, from, next)
2020-04-09 01:04:05 +02:00
{
2020-04-08 23:33:56 +02:00
if (this.dirty)
2020-04-08 16:14:12 +02:00
{
Overlay.confirm({
title: 'You have unsaved changes',
text: 'Are you sure you want to navigate away from this page?',
confirmLabel: '@unsavedchanges.confirm',
closeLabel: '@unsavedchanges.close'
}).then(
() => next(false),
() => next()
);
2020-04-08 23:33:56 +02:00
}
else
{
next()
2020-04-08 16:14:12 +02:00
}
},
2020-04-10 01:17:16 +02:00
// handles a promise
handle(promise)
{
this.setState('loading');
return new Promise((resolve, reject) =>
{
promise
.then(
response =>
{
this.setState('success');
this.setDirty(false);
resolve(response);
},
errors =>
{
this.setState('error');
this.setErrors(errors);
reject(errors);
}
)
.catch(exception =>
{
this.setState('error');
console.info('catch', exception);
// TODO should we throw here, probably show an error overlay
throw exception;
});
});
},
2020-04-08 23:33:56 +02:00
2020-04-09 15:53:03 +02:00
// submits the form
2020-04-10 01:17:16 +02:00
onSubmit(e)
2020-04-08 16:14:12 +02:00
{
2020-04-10 01:17:16 +02:00
this.submit(this, e);
2020-04-08 23:33:56 +02:00
},
2020-04-09 15:53:03 +02:00
// set the form to dirty when one of the fields changes
2020-04-10 01:17:16 +02:00
onChange(e)
2020-04-08 23:33:56 +02:00
{
2020-04-09 15:53:03 +02:00
this.dirty = true;
2020-04-10 01:17:16 +02:00
},
// set dirty status
setDirty(dirty)
{
this.dirty = dirty;
},
// set state for this component and all child components which attached it's state from the slot props
2020-04-10 01:17:16 +02:00
setState(state)
{
2020-04-10 11:12:11 +02:00
this.state = state;
2020-04-09 15:53:03 +02:00
},
// tries to find matching fields for the given errors and displays them
setErrors(errors)
2020-04-09 15:53:03 +02:00
{
if (typeof errors === 'undefined')
{
this.errors = [];
}
else if (!_isArray(errors))
2020-04-09 15:53:03 +02:00
{
this.errors = [errors];
}
else
{
this.errors = errors;
}
let errorGroups = _groupBy(this.errors, 'field');
// find components which can output errors
let traverseChildren = parent =>
{
parent.$children.forEach(component =>
{
2020-04-10 11:12:11 +02:00
const isErrorComponent = this.errorComponents.indexOf(component.$options.name) > -1;
const field = component.field;
if (isErrorComponent && field)
{
let errorGroup = errorGroups[field];
if (errorGroup)
{
component.set(errorGroup);
}
}
else
{
traverseChildren(component);
}
});
};
2020-04-10 01:17:16 +02:00
traverseChildren(this);
}
2020-04-08 16:14:12 +02:00
}
}
</script>