Adding isValid to QForm #16989
-
In the present, we have I have checked the #4111 issue and inside of that there is a link that is broken. XForm: <template>
<q-form>
<slot />
<q-btn
type="submit"
:disable="isValid === false"
label="Submit"
/>
</q-form>
</template> |
Beta Was this translation helpful? Give feedback.
Replies: 6 comments 1 reply
-
this is a really need to have option. |
Beta Was this translation helpful? Give feedback.
-
Yeah, when migrating from Vuetify, we had same need, and finally we gave up and used Quasar approach for validating form. But it would be nice if they add this feature. @Koutena link on #4111 is not broken, you just need to change the branch. As far as I checked |
Beta Was this translation helpful? Give feedback.
-
There is no need to disable the submit button yourself. If you follow the example at https://quasar.dev/vue-components/form#example--basic then that's all that you need. <q-form @submit="onSubmit" @reset="onReset">
<!-- .... -->
<q-btn label="Submit" type="submit" color="primary"/>
<q-btn label="Reset" type="reset" color="primary" flat class="q-ml-sm" />
</q-form> Your onSubmit method will be called only if it passes the validation. Furthermore, this way the user will also be able to hit "enter" and try to submit the form, which is an accessibility feature. Regardless, we can't supply an external isValid because of the nature of the async (and superior) validation process. If the validation were an atomic instruction then it would have been different. We also care about the runtime performance a great deal, and doing this would require to verify the validation on each keystroke -- to make matters worse, you could also have lazy validation which complicates everything even more. What we currently advertise for using is the easiest + consistent + performant option. |
Beta Was this translation helpful? Give feedback.
-
According to UI/UX design requirement, I need to disable the submit button. I know until all validations are not passed, the submit will not happen; But the UI/UX designer has requested me that the submit button should be disable when the form is invalid. |
Beta Was this translation helpful? Give feedback.
-
@rstoenescu Thanks for your suggestion. But please attention to two points:
XForm: <template>
<q-form>
<slot />
<q-btn
type="submit"
:disable="isValid === false"
label="Submit"
/>
</q-form>
</template>
Why don't we have it in Quasar?! |
Beta Was this translation helpful? Give feedback.
-
Finally, I was able to achieve my goal by implementing the following: XForm.vue: <template>
<q-form
ref="xForm"
greedy
:style="{ 'min-height': `calc(100dvh - ${props.height ?? '0'})` }"
>
<div>
<slot />
</div>
<div
class="q-mt-lg"
:class="[
{
bordered: !props.hideActionsBorder,
'absolute-bottom': $q.screen.xs && !isNotSticky,
},
props.isCancelable ? 'grid-2 gap-sm' : 'grid-1',
]"
>
<x-btn-primary
v-if="isCancelable"
outline
class="text-weight-medium rounded-sm"
:label="$t('actions.cancel')"
@click="cancel"
/>
<x-btn-primary
class="text-weight-medium rounded-sm"
type="submit"
:class="$q.screen.gt.sm ? 'font-size-md' : 'font-size-sm'"
:loading="props.loading"
:label="props.submitLabel"
:disable="isNotValid || !props.isValid"
:outline="isOutlineSubmit"
/>
</div>
</q-form>
</template>
<script setup lang="ts">
import { QFieldProps, QForm } from 'quasar';
import {
computed,
getCurrentInstance,
onMounted,
onBeforeUnmount,
ref,
watch,
nextTick,
} from 'vue';
import { useRouter } from 'vue-router';
const props = withDefaults(
defineProps<{
height?: string;
loading?: boolean;
submitLabel: string;
hideActionsBorder?: boolean;
isValid?: boolean;
isCancelable?: boolean;
isNotSticky?: boolean;
isOutlineSubmit?: boolean;
}>(),
{
isValid: true,
}
);
const router = useRouter();
const emit = defineEmits(['cancel']);
const rulesValidityStatuses = ref<boolean[]>([]);
const rulesWatchers = ref<(() => void)[]>([]);
const xForm = ref<QForm | null>(null);
const currentInstance = getCurrentInstance();
function cancel() {
currentInstance?.vnode?.props?.onCancel ? emit('cancel') : router.back();
}
function setupValidation() {
if (xForm.value) {
const components = xForm.value
.getValidationComponents()
.filter((c: unknown) => (c as unknown as QFieldProps).rules);
rulesValidityStatuses.value = new Array(components.length).fill(false);
components.forEach((component, index) => {
const ruleWatcher = watch(
() => (component as QFieldProps).modelValue,
(value) => {
rulesValidityStatuses.value[index] =
(component as unknown as QFieldProps).rules?.every(
(c) => (c as CallableFunction)(value) === true
) ?? false;
},
{ deep: true, immediate: true }
);
rulesWatchers.value.push(ruleWatcher);
});
}
}
function resetRulesWatchers() {
rulesWatchers.value.forEach((cleanup) => cleanup());
rulesWatchers.value = [];
}
const isNotValid = computed<boolean>(() =>
rulesValidityStatuses.value.some((c) => c === false)
);
function observeDOMChanges() {
if (!xForm.value) return;
const observer = new MutationObserver(() => {
nextTick(() => {
resetRulesWatchers();
rulesValidityStatuses.value = [];
setupValidation();
});
});
observer.observe(xForm.value.$el, {
childList: true,
subtree: true,
attributes: true,
characterData: true,
});
return observer;
}
let observer: MutationObserver | undefined;
onMounted(() => {
setupValidation();
observer = observeDOMChanges();
});
onBeforeUnmount(() => {
observer?.disconnect();
resetRulesWatchers();
});
if (import.meta.hot) {
import.meta.hot.on('vite:beforeUpdate', () => {
resetRulesWatchers();
xForm.value?.resetValidation();
rulesValidityStatuses.value = [];
nextTick(() => {
setupValidation();
});
});
}
</script> |
Beta Was this translation helpful? Give feedback.
Finally, I was able to achieve my goal by implementing the following:
XForm.vue: