resources/assets/components/Formfields/DynamicInput/Formfield.vue
<template>
<div v-if="action == 'query'">
</div>
<div v-else-if="action == 'browse'">
</div>
<div v-else-if="action == 'edit' || action == 'add'" class="w-full" :class="options.inline ? 'flex space-x-4' : 'space-y-4'">
<div v-for="(input, num) in inputs" class="w-full" :class="options.outline ? 'input-group' : null">
<label v-if="input.title" class="label">{{ input.title }}</label>
<template v-if="input.type == 'select'">
<select class="input mt-2 w-full" @change="setSelectValue(input, $event)" :multiple="input.multiple" :id="`input-${column.column}-${num}`">
<option
v-for="(title, value) in input.options"
:value="value"
:selected="optionSelected(input, value)"
>
{{ title }}
</option>
</select>
</template>
<template v-else-if="input.type == 'text'">
<input
type="text"
class="input mt-2 w-full"
v-bind:value="getValue(input)"
@input="setValue(input, $event.target.value)"
:placeholder="input.placeholder || ''"
>
</template>
<template v-else-if="input.type == 'number'">
<input
type="number"
class="input mt-2 w-full"
v-bind:value="getValue(input)"
@input="setValue(input, $event.target.value)"
:placeholder="input.placeholder || ''"
:min="input.min"
:max="input.max"
>
</template>
<template v-else-if="input.type == 'checkbox' || input.type == 'radio'">
<template v-for="(title, value) in input.options">
<div class="flex space-x-1.5 items-center">
<input
:type="input.type"
class="input"
:value="value"
@change="setSelectValue(input, $event)"
:id="`${column.column}-${value}`"
:name="`${column.column}-${input.key}`"
:checked="optionSelected(input, value)"
>
<label :for="`${column.column}-${value}`" class="label">{{ title }}</label>
</div>
</template>
</template>
<template v-else-if="input.type == 'switch'">
<div class="flex space-x-1.5 items-center">
<input
type="checkbox"
class="input"
@change="setValue(input, $event.target.checked)"
:id="`${column.column}-${input.key}`"
:checked="getValue(input)"
>
<label :for="`${column.column}-${input.key}`" class="label">{{ input.title }}</label>
</div>
</template>
</div>
</div>
</template>
<script>
import axios from 'axios';
import { debounce } from 'debounce';
import formfield from '@mixins/formfield';
import EventBus from '@/eventbus';
export default {
mixins: [formfield],
data() {
return {
inputs: [],
should: 0,
has: 0,
load: null,
};
},
methods: {
optionSelected(input, value) {
let current = this.sanitizeValue(this.getValue(input));
value = this.sanitizeValue(value);
if (Array.isArray(current)) {
return current.includes(value);
}
return current == value;
},
getValue(input) {
if (input.key === null) {
return this.modelValue || input.value || null;
}
if (this.modelValue) {
return this.modelValue[input.key] || input.value || null;
}
return input.value || null;
},
setSelectValue(input, select) {
let value = null;
if (input.multiple) {
value = [];
if (input.type == 'select') {
Array.from(select.target.options).forEach((option, key) => {
if (option.selected) {
if (option.value === '') {
value.push(null);
} else {
value.push(option.value);
}
}
});
} else if (input.type == 'checkbox') {
value = this.getValue(input);
if (!Array.isArray(value)) {
value = [];
}
if (select.target.checked) {
value.push(select.target.value);
} else {
value = value.filter((v) => v !== select.target.value);
}
}
} else {
value = select.target.value;
if (value === '') {
value = null;
}
}
this.setValue(input, value);
},
setValue(input, value) {
if (this.isNumeric(value)) {
value = parseInt(value);
}
value = this.sanitizeValue(value);
if (input.key === null) {
this.$emit('update:modelValue', value);
} else {
let current = this.modelValue;
if (!current || typeof current !== 'object' || current.constructor !== Object) {
current = {};
}
current[input.key] = value;
this.$emit('update:modelValue', current);
}
},
isNumeric(input) {
return !isNaN(parseFloat(input)) && isFinite(input);
},
sanitizeValue(value) {
if (this.isNumeric(value)) {
return parseInt(value);
}
return value;
}
},
created() {
const loadInputs = debounce((data = {}) => {
if (!this.options.route_name) {
return;
}
try {
this.route(this.options.route_name);
} catch (e) {
new this.$notification(this.__('voyager::formfields.dynamic_input.route_warning', { route: this.options.route_name })).color('red').timeout().show();
return;
}
axios.post(this.route(this.options.route_name), { ...this.modelValue, bread_action: this.action, data })
.then((response) => {
this.inputs = response.data;
// Add default value if the property does not exist
response.data.forEach((input) => {
if (input.key) {
var current = this.modelValue;
if (!current || typeof current !== 'object' || current.constructor !== Object) {
current = {};
}
if (!current.hasOwnProperty(input.key)) {
current[input.key] = input.value;
this.$emit('update:modelValue', current);
}
} else if (this.modelValue === null && input.value) {
this.$emit('update:modelValue', input.value);
}
});
}).catch((response) => {});
}, this.options.debounce || 200, false);
this.$watch(() => this.modelValue, () => {
loadInputs();
}, { immediate: true, deep: true });
this.$watch(() => this.options.route_name, (route, old) => {
if (route !== old) {
loadInputs();
}
});
EventBus.on('output', (data) => {
// TODO: When there is a config option that this formfields should refresh when other formfields changed value.
// Might need to remove immediate: true on the modelValue watcher above and instead call `EventBus.emit('output', this.output)` from Bread/EditAdd when mounted.
// Also consider settings. They have another structure
//this.loadInputs(data);
});
}
}
</script>