DatePicker & Form with v-model & validation in fhcAlert

This commit is contained in:
cgfhtw
2023-11-16 16:41:22 +01:00
parent c65fc571ac
commit b684c6a34d
3 changed files with 152 additions and 103 deletions
@@ -256,6 +256,8 @@ class Student extends FHC_Controller
public function check()
{
$_POST = json_decode($this->input->raw_input_stream, true);
$this->load->library('form_validation');
$this->form_validation->set_rules('gebdatum', 'Geburtsdatum', 'callback_isValidDate', [
@@ -272,7 +274,7 @@ class Student extends FHC_Controller
$gebdatum = $this->input->post('gebdatum');
if (!$vorname && !$nachname && !$gebdatum) {
return $this->outputJsonSuccess([]);
return $this->outputJsonError(['#' => 'At least one of vorname, nachname or gebdatum must be set']);
}
$this->load->model('person/Person_model', 'PersonModel');
@@ -1,7 +1,7 @@
import {CoreRESTClient} from '../../../../RESTClient.js';
import BsModal from '../../../Bootstrap/Modal.js';
import FhcFormValidation from '../../../Form/Validation.js';
import VueDatePicker1 from '../../../vueDatepicker.js.php';
import VueDatePicker from '../../../vueDatepicker.js.php';
var _uuid = 0;
@@ -9,128 +9,98 @@ export default {
components: {
BsModal,
FhcFormValidation,
VueDatePicker1
VueDatePicker
},
props: {
studiengangKz: Number
},
data() {
return {
date: null,
geschlechter: []
geschlechter: [],
formData: {},
suggestions: {},
person: null
}
},
computed: {
saveTitle() {
if (this.person === null)
return 'Bitte auswählen';
if (this.person === 0)
return 'Neue Person anlegen';
return 'zu ' + (this.person.vorname + ' ' + this.person.nachname).trim() + ' hinzufügen';
},
formDataPerson() {
if (this.person)
return this.person;
return this.formData;
}
},
methods: {
open() {
this.$refs.modal.show();
},
send(e) {
this.sendForm(e.target, 'components/stv/student/check')
reset() {
this.formData = {};
this.person = null;
this.suggestions = [];
this.$fhcAlert.resetFormValidation(this.$refs.form)
},
loadSuggestions() {
if (this.person)
return;
// TODO(chris): load serialized
CoreRESTClient
.post('components/stv/student/check', {
vorname: this.formData.vorname,
nachname: this.formData.nachname,
gebdatum: this.formData.gebdatum
})
.then(result => CoreRESTClient.getData(result.data) || [])
.then(result => {
console.log('check: ', result);
this.suggestions = result;
})
.catch(() => {
// NOTE(chris): repeat request
window.setTimeout(this.loadSuggestions, 100);
});
},
sendForm(form, url) {
this.resetFormValidation(form);
const data = new FormData(form);
return CoreRESTClient
.post(url, data)
send(e) {
if (e.person === null)
this.$fhcAlert.alertError('Select a person first'); // TODO(chris): better error handling!
this.$fhcAlert.resetFormValidation(form);
const data = {...this.formData, ...(this.person || {})};
CoreRESTClient
.post('components/stv/student/add')
.then(result => result.data)
.then(result => {
if (CoreRESTClient.isError(result))
throw new Error(CoreRESTClient.getError(result));
return result;
return CoreRESTClient.getData(result);
})
.catch(this.handleErrors(form));
},
resetForm() {
const form = this.$refs.form;
form.reset();
this.resetFormValidation(form);
},
resetFormValidation(form) {
const event = new Event('fhc-form-reset');
form.querySelectorAll(['[data-fhc-form-validate],[data-fhc-form-error]']).forEach(el => el.dispatchEvent(event));
/*const alert = form.querySelector('div.alert.alert-danger[role="alert"]');
if (alert) {
alert.innerHTML = '';
alert.classList.add('d-none');
}
form.querySelectorAll('.invalid-feedback').forEach(n => n.remove());
form.querySelectorAll('.is-invalid').forEach(n => n.classList.remove('is-invalid'));
form.querySelectorAll('.is-valid').forEach(n => n.classList.remove('is-valid'));*/
},
handleErrors(error, form) {
if (form === undefined) {
if (error && error.nodeType === Node.ELEMENT_NODE)
return err => this.handleErrors(err, error);
} else {
if (error?.response?.status == 400) {
let errors = CoreRESTClient.getError(error.response.data);
if (typeof errors !== "object")
errors = error.response.data;
// NOTE(chris): reset form validation
this.resetFormValidation(form);
// NOTE(chris): set form input validation
const notFound = Object.entries(errors).filter(([key, detail]) => {
const input = form.querySelector('[data-fhc-form-validate="' + key + '"]');
if (!input)
return true;
input.dispatchEvent(new CustomEvent('fhc-form-invalidate', {detail}));
/*const input = form.querySelector('[name="' + key + '"]');
if (!input)
return true;
input.classList.add('is-invalid');
const feedback = document.createElement('div');
feedback.classList.add('invalid-feedback');
feedback.innerHTML = detail;
input.after(feedback);*/
return false;
}).map(arr => arr[1]);
//const alert = form.querySelector('div.alert.alert-danger[role="alert"]');
const alert = form.querySelector('[data-fhc-form-error]');
if (alert && notFound.length) {
alert.dispatchEvent(new CustomEvent('fhc-form-error', {detail: notFound}));
/*notFound.forEach(txt => {
const p = document.createElement('p');
p.innerHTML = txt;
alert.append(p);
});
if (notFound.length) {
alert.lastChild.classList.add('mb-0');
alert.classList.remove('d-none');
}*/
} else {
notFound.forEach(this.$fhcAlert.alertError);
}
return;
}
}
this.$fhcAlert.handleSystemError(error);
},test() {console.log('test')}
.then(result => {
this.$fhcAlert.alertSuccess('Gespeichert');
console.log('saved');
// TODO(chris): close
})
.catch(this.$fhcAlert.handleFormValidation);
}
},
created() {
this.uuid = _uuid++;
// TODO(chris): geschlechter in parent?
CoreRESTClient
.get('components/stv/Student/getGeschlechter')
.then(result => CoreRESTClient.getData(result.data))
.then(result => {
this.geschlechter = result.data;
this.geschlechter = result;
})
.catch(err => {
console.error(CoreRestClient.getError(err.response.data) || err.message);
});
.catch(this.$fhcAlert.handleSystemError);
},
template: `
<form ref="form" class="stv-list-new" @submit.prevent="send">
<bs-modal ref="modal" dialog-class="modal-fullscreen" @hidden-bs-modal="resetForm">
<bs-modal ref="modal" dialog-class="modal-fullscreen" @hidden-bs-modal="reset">
<template #title>
InteressentIn anlegen
</template>
@@ -140,19 +110,19 @@ export default {
<div class="col-sm-4 mb-3">
<label :for="'stv-list-new-anrede-' + uuid">Anrede</label>
<fhc-form-validation name="anrede">
<input :id="'stv-list-new-anrede-' + uuid" type="text" name="anrede" class="form-control">
<input :id="'stv-list-new-anrede-' + uuid" type="text" name="anrede" v-model="formDataPerson['anrede']" class="form-control" :disabled="person">
</fhc-form-validation>
</div>
<div class="col-sm-4 mb-3">
<label :for="'stv-list-new-titelpre-' + uuid">Titel (Pre)</label>
<fhc-form-validation name="titelpre">
<input :id="'stv-list-new-titelpre-' + uuid" type="text" name="titelpre" class="form-control">
<input :id="'stv-list-new-titelpre-' + uuid" type="text" name="titelpre" v-model="formDataPerson['titelpre']" class="form-control" :disabled="person">
</fhc-form-validation>
</div>
<div class="col-sm-4 mb-3">
<label :for="'stv-list-new-titelpost-' + uuid">Titel (Post)</label>
<fhc-form-validation name="titelpost">
<input :id="'stv-list-new-titelpost-' + uuid" type="text" name="titelpost" class="form-control">
<input :id="'stv-list-new-titelpost-' + uuid" type="text" name="titelpost" v-model="formDataPerson['titelpost']" class="form-control" :disabled="person">
</fhc-form-validation>
</div>
</div>
@@ -160,19 +130,19 @@ export default {
<div class="col-sm-4 mb-3">
<label :for="'stv-list-new-nachname-' + uuid">Nachname*</label>
<fhc-form-validation name="nachname">
<input :id="'stv-list-new-nachname-' + uuid" type="text" name="nachname" class="form-control">
<input :id="'stv-list-new-nachname-' + uuid" type="text" name="nachname" v-model="formDataPerson['nachname']" class="form-control" :disabled="person" @input="loadSuggestions">
</fhc-form-validation>
</div>
<div class="col-sm-4 mb-3">
<label :for="'stv-list-new-vorname-' + uuid">Vorname</label>
<fhc-form-validation name="vorname">
<input :id="'stv-list-new-vorname-' + uuid" type="text" name="vorname" class="form-control">
<input :id="'stv-list-new-vorname-' + uuid" type="text" name="vorname" v-model="formDataPerson['vorname']" class="form-control" :disabled="person" @input="loadSuggestions">
</fhc-form-validation>
</div>
<div class="col-sm-4 mb-3">
<label :for="'stv-list-new-vornamen-' + uuid">Weitere Vornamen</label>
<fhc-form-validation name="vornamen">
<input :id="'stv-list-new-vornamen-' + uuid" type="text" name="vornamen" class="form-control">
<input :id="'stv-list-new-vornamen-' + uuid" type="text" name="vornamen" v-model="formDataPerson['vornamen']" class="form-control" :disabled="person">
</fhc-form-validation>
</div>
</div>
@@ -180,7 +150,7 @@ export default {
<div class="col-sm-4 mb-3">
<label :for="'stv-list-new-wahlname-' + uuid">Wahlname</label>
<fhc-form-validation name="wahlname">
<input :id="'stv-list-new-wahlname-' + uuid" type="text" name="wahlname" class="form-control">
<input :id="'stv-list-new-wahlname-' + uuid" type="text" name="wahlname" v-model="formDataPerson['wahlname']" class="form-control" :disabled="person">
</fhc-form-validation>
</div>
</div>
@@ -188,7 +158,7 @@ export default {
<div class="col-sm-4 mb-3">
<label :for="'stv-list-new-geschlecht-' + uuid">Geschlecht*</label>
<fhc-form-validation name="geschlecht">
<select :id="'stv-list-new-geschlecht-' + uuid" class="form-control" name="geschlecht">
<select :id="'stv-list-new-geschlecht-' + uuid" class="form-control" :disabled="person" name="geschlecht" v-model="formDataPerson['geschlecht']">
<option v-for="geschlecht in geschlechter" :key="geschlecht.geschlecht" :value="geschlecht.geschlecht">{{geschlecht.bezeichnung}}</option>
</select>
</fhc-form-validation>
@@ -196,13 +166,23 @@ export default {
<div class="col-sm-4 mb-3">
<label :for="'dp-input-stv-list-new-gebdatum-' + uuid">Geburtsdatum</label>
<fhc-form-validation name="gebdatum">
<vue-date-picker1 :uid="'stv-list-new-gebdatum-' + uuid" name="gebdatum" text-input auto-apply no-today v-model="date" :enable-time-picker="false" format="dd.MM.yyyy"></vue-date-picker1>
<vue-date-picker :uid="'stv-list-new-gebdatum-' + uuid" name="gebdatum" text-input auto-apply no-today v-model="formDataPerson['gebdatum']" :enable-time-picker="false" format="dd.MM.yyyy" @update:model-value="loadSuggestions" :disabled="person"></vue-date-picker>
</fhc-form-validation>
</div>
</div>
</template>
<template #footer>
<button type="submit" class="btn btn-primary">Vorschläge laden</button>
<div class="btn-group dropup">
<button type="button" class="btn btn-primary dropdown-toggle dropdown-toggle-split" style="border-bottom-right-radius: 0; border-top-right-radius: 0" data-bs-toggle="dropdown" aria-expanded="false">
<span class="visually-hidden">Choose Person</span>
</button>
<ul class="dropdown-menu">
<!-- TODO(chris): more details -->
<li v-for="suggestion in suggestions" :key="suggestion.person_id"><a class="dropdown-item" :class="{active: person?.person_id == suggestion.person_id}" href="#" @click.prevent="person=suggestion">{{suggestion.vorname + ' ' + suggestion.nachname}}</a></li>
<li><a class="dropdown-item" :class="{active: person === 0}" href="#" @click.prevent="person=0">Neue Person anlegen</a></li>
</ul>
<button type="submit" class="btn btn-primary" :disabled="person === null">{{saveTitle}}</button>
</div>
</template>
</bs-modal>
</form>`
+67
View File
@@ -300,6 +300,73 @@ export default {
// Fallback
fhcerror.alertSystemError('alertSystemError throws Generic Error\r\nError Controller Path: ' + FHC_JS_DATA_STORAGE_OBJECT.called_path + '/' + FHC_JS_DATA_STORAGE_OBJECT.called_method);
},
resetFormValidation(form) {
const event = new Event('fhc-form-reset');
form.querySelectorAll(['[data-fhc-form-validate],[data-fhc-form-error]']).forEach(el => el.dispatchEvent(event));
/*const alert = form.querySelector('div.alert.alert-danger[role="alert"]');
if (alert) {
alert.innerHTML = '';
alert.classList.add('d-none');
}
form.querySelectorAll('.invalid-feedback').forEach(n => n.remove());
form.querySelectorAll('.is-invalid').forEach(n => n.classList.remove('is-invalid'));
form.querySelectorAll('.is-valid').forEach(n => n.classList.remove('is-valid'));*/
},
handleFormValidation(error, form) {
if (form === undefined) {
if (error && error.nodeType === Node.ELEMENT_NODE)
return err => $fhcAlert.handleFormValidation(err, error);
} else {
if (error?.response?.status == 400) {
let errors = CoreRESTClient.getError(error.response.data);
if (typeof errors !== "object")
errors = error.response.data;
// NOTE(chris): reset form validation
$fhcAlert.resetFormValidation(form);
// NOTE(chris): set form input validation
const notFound = Object.entries(errors).filter(([key, detail]) => {
const input = form.querySelector('[data-fhc-form-validate="' + key + '"]');
if (!input)
return true;
input.dispatchEvent(new CustomEvent('fhc-form-invalidate', {detail}));
/*const input = form.querySelector('[name="' + key + '"]');
if (!input)
return true;
input.classList.add('is-invalid');
const feedback = document.createElement('div');
feedback.classList.add('invalid-feedback');
feedback.innerHTML = detail;
input.after(feedback);*/
return false;
}).map(arr => arr[1]);
//const alert = form.querySelector('div.alert.alert-danger[role="alert"]');
const alert = form.querySelector('[data-fhc-form-error]');
if (alert && notFound.length) {
alert.dispatchEvent(new CustomEvent('fhc-form-error', {detail: notFound}));
/*notFound.forEach(txt => {
const p = document.createElement('p');
p.innerHTML = txt;
alert.append(p);
});
if (notFound.length) {
alert.lastChild.classList.add('mb-0');
alert.classList.remove('d-none');
}*/
} else {
notFound.forEach($fhcAlert.alertError);
}
return;
}
}
$fhcAlert.handleSystemError(error);
}
};
app.config.globalProperties.$fhcAlert = $fhcAlert;