class Errors {
/** * Create a new Errors instance. */ constructor() { this.errors = {}; } /** * Determine if an errors exists for the given field. * * @param {string} field */ has(field) { return this.errors.hasOwnProperty(field); } /** * Determine if we have any errors. */ any() { return Object.keys(this.errors).length > 0; } /** * Retrieve the error message for a field. * * @param {string} field */ get(field) { if (this.errors[field]) { return this.errors[field][0]; } } /** * Record the new errors. * * @param {object} errors */ record(errors) { this.errors = errors; } /** * Clear one or all error fields. * * @param {string|null} field */ clear(field) { if (field) { delete this.errors[field]; return; } this.errors = {}; } } class Form { /** * Create a new Form instance. * * @param {object} data */ constructor(data) { this.originalData = data; for (let field in data) { this[field] = data[field]; } this.errors = new Errors(); } /** * Fetch all relevant data for the form. */ data() { let data = {}; for (let property in this.originalData) { data[property] = this[property]; } return data; } /** * Reset the form fields. */ reset() { for (let field in this.originalData) { this[field] = ''; } this.errors.clear(); } /** * Send a POST request to the given URL. * . * @param {string} url */ post(url) { return this.submit('post', url); } /** * Send a PUT request to the given URL. * . * @param {string} url */ put(url) { return this.submit('put', url); } /** * Send a PATCH request to the given URL. * . * @param {string} url */ patch(url) { return this.submit('patch', url); } /** * Send a DELETE request to the given URL. * . * @param {string} url */ delete(url) { return this.submit('delete', url); } /** * Submit the form. * * @param {string} requestType * @param {string} url */ submit(requestType, url) { return new Promise((resolve, reject) => { axios[requestType](url, this.data()) .then(response => { this.onSuccess(response.data); resolve(response.data); }) .catch(error => { this.onFail(error.response.data); reject(error.response.data); }); }); } /** * Handle a successful form submission. * * @param {object} data */ onSuccess(data) { alert(data.message); // temporary this.reset(); } /** * Handle a failed form submission. * * @param {object} errors */ onFail(errors) { this.errors.record(errors); } } new Vue({ el: '#app', data: { form: new Form({ name: '', description: '' }) }, methods: { onSubmit() { this.form.post('/projects') .then(response => alert('Wahoo!')); } } });
<form method="POST" action="/projects" @submit.prevent="onSubmit" @keydown="form.errors.clear($event.target.name)">
<div class="control">
<label for="name" class="label">Project Name:</label>
<input type="text" id="name" name="name" class="input" v-model="form.name">
<span class="help is-danger" v-if="form.errors.has('name')" v-text="form.errors.get('name')"></span>
</div>
<div class="control">
<label for="description" class="label">Project Description:</label>
<input type="text" id="description" name="description" class="input" v-model="form.description">
<span class="help is-danger" v-if="form.errors.has('description')" v-text="form.errors.get('description')"></span>
</div>
<div class="control">
<button class="button is-primary" :disabled="form.errors.any()">Create</button>
</div>
</form>