Convert to composition API

This commit is contained in:
Mert Sincan 2024-10-23 22:57:52 +01:00
parent eaa03d8e72
commit d2b191ed6e
8 changed files with 109 additions and 172 deletions

View file

@ -12,7 +12,7 @@
</DynamicFormField> </DynamicFormField>
<DynamicFormField groupId="passId_1" name="password"> <DynamicFormField groupId="passId_1" name="password">
<DynamicFormLabel>Password</DynamicFormLabel> <DynamicFormLabel>Password</DynamicFormLabel>
<DynamicFormControl as="Password" :feedback="false" fluid :schema="passwordSchema" /> <DynamicFormControl as="Password" :feedback="false" toggleMask fluid :schema="passwordSchema" />
<DynamicFormMessage errorType="minimum" /> <DynamicFormMessage errorType="minimum" />
<DynamicFormMessage errorType="maximum" /> <DynamicFormMessage errorType="maximum" />
<DynamicFormMessage errorType="uppercase" severity="warn" /> <DynamicFormMessage errorType="uppercase" severity="warn" />
@ -73,6 +73,7 @@ export default {
label: 'Password', label: 'Password',
as: 'Password', as: 'Password',
feedback: false, feedback: false,
toggleMask: true,
fluid: true, fluid: true,
messages: [{ errorType: 'minimum' }, { errorType: 'maximum' }, { errorType: 'uppercase', severity: 'warn' }, { errorType: 'lowercase', severity: 'warn' }, { errorType: 'number', severity: 'secondary' }], messages: [{ errorType: 'minimum' }, { errorType: 'maximum' }, { errorType: 'uppercase', severity: 'warn' }, { errorType: 'lowercase', severity: 'warn' }, { errorType: 'number', severity: 'secondary' }],
schema: z schema: z
@ -104,7 +105,7 @@ export default {
</DynamicFormField> </DynamicFormField>
<DynamicFormField groupId="passId_1" name="password"> <DynamicFormField groupId="passId_1" name="password">
<DynamicFormLabel>Password</DynamicFormLabel> <DynamicFormLabel>Password</DynamicFormLabel>
<DynamicFormControl as="Password" :feedback="false" fluid :schema="passwordSchema" /> <DynamicFormControl as="Password" :feedback="false" toggleMask fluid :schema="passwordSchema" />
<DynamicFormMessage errorType="minimum" /> <DynamicFormMessage errorType="minimum" />
<DynamicFormMessage errorType="maximum" /> <DynamicFormMessage errorType="maximum" />
<DynamicFormMessage errorType="uppercase" severity="warn" /> <DynamicFormMessage errorType="uppercase" severity="warn" />
@ -133,7 +134,7 @@ export default {
</DynamicFormField> </DynamicFormField>
<DynamicFormField groupId="passId_1" name="password"> <DynamicFormField groupId="passId_1" name="password">
<DynamicFormLabel>Password</DynamicFormLabel> <DynamicFormLabel>Password</DynamicFormLabel>
<DynamicFormControl as="Password" :feedback="false" fluid :schema="passwordSchema" /> <DynamicFormControl as="Password" :feedback="false" toggleMask fluid :schema="passwordSchema" />
<DynamicFormMessage errorType="minimum" /> <DynamicFormMessage errorType="minimum" />
<DynamicFormMessage errorType="maximum" /> <DynamicFormMessage errorType="maximum" />
<DynamicFormMessage errorType="uppercase" severity="warn" /> <DynamicFormMessage errorType="uppercase" severity="warn" />
@ -193,6 +194,7 @@ export default {
label: 'Password', label: 'Password',
as: 'Password', as: 'Password',
feedback: false, feedback: false,
toggleMask: true,
fluid: true, fluid: true,
messages: [{ errorType: 'minimum' }, { errorType: 'maximum' }, { errorType: 'uppercase', severity: 'warn' }, { errorType: 'lowercase', severity: 'warn' }, { errorType: 'number', severity: 'secondary' }], messages: [{ errorType: 'minimum' }, { errorType: 'maximum' }, { errorType: 'uppercase', severity: 'warn' }, { errorType: 'lowercase', severity: 'warn' }, { errorType: 'number', severity: 'secondary' }],
schema: z schema: z
@ -247,7 +249,7 @@ export default {
</DynamicFormField> </DynamicFormField>
<DynamicFormField groupId="passId_1" name="password"> <DynamicFormField groupId="passId_1" name="password">
<DynamicFormLabel>Password</DynamicFormLabel> <DynamicFormLabel>Password</DynamicFormLabel>
<DynamicFormControl as="Password" :feedback="false" fluid :schema="passwordSchema" /> <DynamicFormControl as="Password" :feedback="false" toggleMask fluid :schema="passwordSchema" />
<DynamicFormMessage errorType="minimum" /> <DynamicFormMessage errorType="minimum" />
<DynamicFormMessage errorType="maximum" /> <DynamicFormMessage errorType="maximum" />
<DynamicFormMessage errorType="uppercase" severity="warn" /> <DynamicFormMessage errorType="uppercase" severity="warn" />
@ -309,6 +311,7 @@ const fields = reactive({
label: 'Password', label: 'Password',
as: 'Password', as: 'Password',
feedback: false, feedback: false,
toggleMask: true,
fluid: true, fluid: true,
messages: [ messages: [
{ errorType: 'minimum' }, { errorType: 'minimum' },

View file

@ -3,13 +3,13 @@
<p>The <i>submit</i> callback provides an object containing the form's validity, all errors, and current states. This offers access to the form values as well as validation status and any existing errors during submission.</p> <p>The <i>submit</i> callback provides an object containing the form's validity, all errors, and current states. This offers access to the form values as well as validation status and any existing errors during submission.</p>
</DocSectionText> </DocSectionText>
<div class="card flex justify-center"> <div class="card flex justify-center">
<Form v-slot="$form" :initialValues :resolver @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-56"> <Form v-slot="$form" :initialValues :resolver @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-60">
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<InputText name="username" type="text" placeholder="Username" fluid /> <InputText name="username" type="text" placeholder="Username" fluid />
<Message v-if="$form.username?.invalid" severity="error">{{ $form.username.error.message }}</Message> <Message v-if="$form.username?.invalid" severity="error">{{ $form.username.error.message }}</Message>
</div> </div>
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<Password name="password" placeholder="Password" :feedback="false" fluid /> <Password name="password" placeholder="Password" :feedback="false" toggleMask fluid />
<Message v-if="$form.password?.invalid" severity="error"> <Message v-if="$form.password?.invalid" severity="error">
<ul class="mx-1 px-3"> <ul class="mx-1 px-3">
<li v-for="(error, index) of $form.password.errors" :key="index" class="py-1">{{ error.message }}</li> <li v-for="(error, index) of $form.password.errors" :key="index" class="py-1">{{ error.message }}</li>
@ -53,13 +53,13 @@ export default {
), ),
code: { code: {
basic: ` basic: `
<Form v-slot="$form" :initialValues :resolver @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-56"> <Form v-slot="$form" :initialValues :resolver @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-60">
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<InputText name="username" type="text" placeholder="Username" fluid /> <InputText name="username" type="text" placeholder="Username" fluid />
<Message v-if="$form.username?.invalid" severity="error">{{ $form.username.error.message }}</Message> <Message v-if="$form.username?.invalid" severity="error">{{ $form.username.error.message }}</Message>
</div> </div>
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<Password name="password" placeholder="Password" :feedback="false" fluid /> <Password name="password" placeholder="Password" :feedback="false" toggleMask fluid />
<Message v-if="$form.password?.invalid" severity="error"> <Message v-if="$form.password?.invalid" severity="error">
<ul class="mx-1 px-3"> <ul class="mx-1 px-3">
<li v-for="(error, index) of $form.password.errors" :key="index" class="py-1">{{ error.message }}</li> <li v-for="(error, index) of $form.password.errors" :key="index" class="py-1">{{ error.message }}</li>
@ -74,13 +74,13 @@ export default {
<div class="card flex justify-center"> <div class="card flex justify-center">
<Toast /> <Toast />
<Form v-slot="$form" :initialValues :resolver @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-56"> <Form v-slot="$form" :initialValues :resolver @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-60">
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<InputText name="username" type="text" placeholder="Username" fluid /> <InputText name="username" type="text" placeholder="Username" fluid />
<Message v-if="$form.username?.invalid" severity="error">{{ $form.username.error.message }}</Message> <Message v-if="$form.username?.invalid" severity="error">{{ $form.username.error.message }}</Message>
</div> </div>
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<Password name="password" placeholder="Password" :feedback="false" fluid /> <Password name="password" placeholder="Password" :feedback="false" toggleMask fluid />
<Message v-if="$form.password?.invalid" severity="error"> <Message v-if="$form.password?.invalid" severity="error">
<ul class="mx-1 px-3"> <ul class="mx-1 px-3">
<li v-for="(error, index) of $form.password.errors" :key="index" class="py-1">{{ error.message }}</li> <li v-for="(error, index) of $form.password.errors" :key="index" class="py-1">{{ error.message }}</li>
@ -145,13 +145,13 @@ export default {
<div class="card flex justify-center"> <div class="card flex justify-center">
<Toast /> <Toast />
<Form v-slot="$form" :initialValues :resolver @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-56"> <Form v-slot="$form" :initialValues :resolver @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-60">
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<InputText name="username" type="text" placeholder="Username" fluid /> <InputText name="username" type="text" placeholder="Username" fluid />
<Message v-if="$form.username?.invalid" severity="error">{{ $form.username.error.message }}</Message> <Message v-if="$form.username?.invalid" severity="error">{{ $form.username.error.message }}</Message>
</div> </div>
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<Password name="password" placeholder="Password" :feedback="false" fluid /> <Password name="password" placeholder="Password" :feedback="false" toggleMask fluid />
<Message v-if="$form.password?.invalid" severity="error"> <Message v-if="$form.password?.invalid" severity="error">
<ul class="mx-1 px-3"> <ul class="mx-1 px-3">
<li v-for="(error, index) of $form.password.errors" :key="index" class="py-1">{{ error.message }}</li> <li v-for="(error, index) of $form.password.errors" :key="index" class="py-1">{{ error.message }}</li>

View file

@ -13,7 +13,7 @@
</Form> </Form>
</template> </template>
<script> <script setup>
import { isNotEmpty } from '@primeuix/utils'; import { isNotEmpty } from '@primeuix/utils';
import { zodResolver } from '@primevue/form/resolvers'; import { zodResolver } from '@primevue/form/resolvers';
import { z } from 'zod'; import { z } from 'zod';
@ -23,43 +23,24 @@ import DynamicFormLabel from './DynamicFormLabel.vue';
import DynamicFormMessage from './DynamicFormMessage.vue'; import DynamicFormMessage from './DynamicFormMessage.vue';
import DynamicFormSubmit from './DynamicFormSubmit.vue'; import DynamicFormSubmit from './DynamicFormSubmit.vue';
export default { const props = defineProps({
name: 'DynamicForm', fields: Object
emits: ['submit'], });
props: {
fields: Object const emit = defineEmits(['submit']);
},
provide() { const defaultValues = ref({});
return { const schemas = ref({});
$fcDynamicForm: this
}; const resolver = computed(() => (isNotEmpty(schemas.value) ? zodResolver(z.object(schemas.value)) : undefined));
}, const initialValues = computed(() => defaultValues.value);
data() {
return { const addField = (name, schema, defaultValue) => {
defaultValues: {}, schema && (schemas.value[name] = schema);
schemas: {} defaultValues.value[name] = defaultValue;
};
},
methods: {
addField(name, schema, defaultValue) {
schema && (this.schemas[name] = schema);
this.defaultValues[name] = defaultValue;
}
},
computed: {
resolver() {
return isNotEmpty(this.schemas) ? zodResolver(z.object(this.schemas)) : undefined;
},
initialValues() {
return this.defaultValues;
}
},
components: {
DynamicFormControl,
DynamicFormField,
DynamicFormLabel,
DynamicFormMessage,
DynamicFormSubmit
}
}; };
provide('$fcDynamicForm', {
addField
});
</script> </script>

View file

@ -2,42 +2,27 @@
<component :is="component" :id :name class="w-full" /> <component :is="component" :id :name class="w-full" />
</template> </template>
<script> <script setup>
import * as PrimeVue from 'primevue/primevue'; import * as PrimeVue from 'primevue/primevue';
import { computed, inject } from 'vue';
export default { const props = defineProps({
name: 'DynamicFormControl', as: {
props: { type: String,
as: { default: 'InputText'
type: String,
default: 'InputText'
},
schema: null,
defaultValue: {
default: ''
}
}, },
inject: { schema: null,
$fcDynamicForm: { defaultValue: {
default: undefined default: ''
},
$fcDynamicFormField: {
default: undefined
}
},
created() {
this.$fcDynamicForm?.addField(this.name, this.schema, this.defaultValue);
},
computed: {
id() {
return this.$fcDynamicFormField?.$props.groupId;
},
name() {
return this.$fcDynamicFormField?.$props.name;
},
component() {
return PrimeVue[this.as] ?? this.as;
}
} }
}; });
const $fcDynamicForm = inject('$fcDynamicForm', undefined);
const $fcDynamicFormField = inject('$fcDynamicFormField', undefined);
const id = computed(() => $fcDynamicFormField?.groupId);
const name = computed(() => $fcDynamicFormField?.name);
const component = computed(() => PrimeVue[props.as] ?? props.as);
$fcDynamicForm?.addField(name.value, props.schema, props.defaultValue);
</script> </script>

View file

@ -4,23 +4,22 @@
</div> </div>
</template> </template>
<script> <script setup>
export default { import { provide } from 'vue';
name: 'DynamicFormField',
props: { const props = defineProps({
groupId: { groupId: {
type: String, type: String,
default: undefined default: undefined
},
name: {
type: String,
default: undefined
}
}, },
provide() { name: {
return { type: String,
$fcDynamicFormField: this default: undefined
};
} }
}; });
provide('$fcDynamicFormField', {
groupId: props.groupId,
name: props.name
});
</script> </script>

View file

@ -4,18 +4,10 @@
</label> </label>
</template> </template>
<script> <script setup>
export default { import { computed, inject } from 'vue';
name: 'DynamicFormLabel',
inject: { const $fcDynamicFormField = inject('$fcDynamicFormField', undefined);
$fcDynamicFormField: {
default: undefined const htmlFor = computed(() => $fcDynamicFormField?.groupId);
}
},
computed: {
htmlFor() {
return this.$fcDynamicFormField?.$props.groupId;
}
}
};
</script> </script>

View file

@ -4,51 +4,29 @@
</Message> </Message>
</template> </template>
<script> <script setup>
import { isNotEmpty } from '@primeuix/utils'; import { isNotEmpty } from '@primeuix/utils';
import { computed, inject } from 'vue';
export default { const props = defineProps({
name: 'DynamicFormLabel', errorType: {
props: { type: String,
errorType: { default: undefined
type: String,
default: undefined
},
severity: {
type: String,
default: 'error'
}
}, },
inject: { severity: {
$pcForm: { type: String,
default: undefined default: 'error'
},
$fcDynamicFormField: {
default: undefined
}
},
computed: {
visible() {
return this.invalid && (this.error || this.errorType === undefined);
},
message() {
return this.errorType ? this.error?.message : this.errors?.[0]?.message;
},
error() {
return this.errors?.find((error) => error.errorType === this.errorType || isNotEmpty(error[this.errorType]));
},
fieldName() {
return this.$fcDynamicFormField?.$props.name;
},
state() {
return this.$pcForm?.states?.[this.fieldName];
},
errors() {
return this.state?.errors;
},
invalid() {
return this.state?.invalid;
}
} }
}; });
const $pcForm = inject('$pcForm', undefined); // Inject PrimeVue Form component
const $fcDynamicFormField = inject('$fcDynamicFormField', undefined);
const fieldName = computed(() => $fcDynamicFormField?.name);
const state = computed(() => $pcForm?.states?.[fieldName.value]);
const errors = computed(() => state.value?.errors);
const invalid = computed(() => state.value?.invalid);
const error = computed(() => errors.value?.find((error) => props.errorType === error.errorType || isNotEmpty(error[props.errorType])));
const message = computed(() => (props.errorType ? error.value?.message : errors.value?.[0]?.message));
const visible = computed(() => invalid.value && (error.value || props.errorType === undefined));
</script> </script>

View file

@ -2,18 +2,17 @@
<Button type="submit" :severity :label /> <Button type="submit" :severity :label />
</template> </template>
<script> <script setup>
export default { import Button from 'primevue/button';
name: 'DynamicFormSubmit',
props: { const props = defineProps({
severity: { severity: {
type: String, type: String,
default: 'secondary' default: 'secondary'
}, },
label: { label: {
type: String, type: String,
default: 'Submit' default: 'Submit'
}
} }
}; });
</script> </script>