@volverjs/form-vue 1.0.0-beta.9 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  <div align="center">
2
-
2
+
3
3
  [![volverjs](docs/static/volverjs-form.svg)](https://volverjs.github.io/form-vue)
4
4
 
5
5
  ## @volverjs/form-vue
@@ -47,17 +47,21 @@ import { createForm } from '@volverjs/form-vue'
47
47
  import { z } from 'zod'
48
48
 
49
49
  const schema = z.object({
50
- firstName: z.string(),
51
- lastName: z.string()
50
+ firstName: z.string(),
51
+ lastName: z.string()
52
52
  })
53
53
 
54
54
  const app = createApp(App)
55
55
  const form = createForm({
56
- schema
57
- // lazyLoad: boolean - default false
58
- // updateThrottle: number - default 500
59
- // continuosValidation: boolean - default false
60
- // sideEffects?: (data: any) => void
56
+ schema
57
+ // lazyLoad: boolean - default false
58
+ // updateThrottle: number - default 500
59
+ // continuousValidation: boolean - default false
60
+ // sideEffects?: (data: any) => void
61
+ // scope?: string - Defines a unique scope for the form instance (singletons)
62
+ // class?: new (data?: any) => Type - Type constructor for form data
63
+ // Example:
64
+ // class: class User { constructor(data?: any) { Object.assign(this, data) } }
61
65
  })
62
66
 
63
67
  app.use(form)
@@ -73,19 +77,21 @@ A `valid` or `invalid` event is emitted when the form status changes.
73
77
 
74
78
  ```vue
75
79
  <script lang="ts" setup>
76
- const onSubmit = (formData) => {
80
+ function onSubmit(formData) {
77
81
  // ...
78
- }
79
- const onInvalid = (errors) => {
82
+ }
83
+ function onInvalid(errors) {
80
84
  // ...
81
- }
85
+ }
82
86
  </script>
83
87
 
84
88
  <template>
85
- <VvForm @submit="onSubmit" @invalid="onInvalid">
86
- <!-- ... -->
87
- <button type="submit">Submit</button>
88
- </VvForm>
89
+ <VvForm @submit="onSubmit" @invalid="onInvalid">
90
+ <!-- ... -->
91
+ <button type="submit">
92
+ Submit
93
+ </button>
94
+ </VvForm>
89
95
  </template>
90
96
  ```
91
97
 
@@ -93,23 +99,25 @@ The submit can be triggered programmatically with the `submit()` method.
93
99
 
94
100
  ```vue
95
101
  <script lang="ts" setup>
96
- import { ref } from 'vue'
97
- import type { FormComponent } from '@volverjs/form-vue'
102
+ import { ref } from 'vue'
103
+ import type { FormComponent } from '@volverjs/form-vue'
98
104
 
99
- const formEl = ref<InstanceType<FormComponent>>(null)
100
- const onSubmit = (formData) => {
105
+ const formEl = ref<InstanceType<FormComponent>>(null)
106
+ function onSubmit(formData) {
101
107
  // ...
102
- }
103
- const submitForm = () => {
108
+ }
109
+ function submitForm() {
104
110
  formEl.value.submit()
105
- }
111
+ }
106
112
  </script>
107
113
 
108
114
  <template>
109
- <VvForm @submit="onSubmit" ref="formEl">
115
+ <VvForm ref="formEl" @submit="onSubmit">
110
116
  <!-- ... -->
111
- </VvForm>
112
- <button type="button" @click.stop="submitForm">Submit</button>
117
+ </VvForm>
118
+ <button type="button" @click.stop="submitForm">
119
+ Submit
120
+ </button>
113
121
  </template>
114
122
  ```
115
123
 
@@ -118,22 +126,22 @@ Use the `v-model` directive (or only `:model-value` to set the initial value of
118
126
  The form data two way binding is **throttled** by default (500ms) to avoid performance issues. The throttle can be changed with the `updateThrottle` option or prop.
119
127
 
120
128
  By default form validation **stops** when a **valid state** is reached.
121
- To activate **continuos validation** use the `continuosValidation` option or prop.
129
+ To activate **continuous validation** use the `continuousValidation` option or prop.
122
130
 
123
131
  ```vue
124
132
  <script lang="ts" setup>
125
- import { ref } from 'vue'
133
+ import { ref } from 'vue'
126
134
 
127
- const formData = ref({
135
+ const formData = ref({
128
136
  firstName: '',
129
137
  lastName: ''
130
- })
138
+ })
131
139
  </script>
132
140
 
133
141
  <template>
134
- <VvForm v-model="formData" :update-throttle="1000" continuos-validation>
142
+ <VvForm v-model="formData" :update-throttle="1000" continuous-validation>
135
143
  <!-- ... -->
136
- </VvForm>
144
+ </VvForm>
137
145
  </template>
138
146
  ```
139
147
 
@@ -144,27 +152,29 @@ The **default settings** are **inherited** from the plugin (if it's installed).
144
152
 
145
153
  ```vue
146
154
  <script lang="ts" setup>
147
- import { useForm } from '@volverjs/form-vue'
148
- import { z } from 'zod'
155
+ import { useForm } from '@volverjs/form-vue'
156
+ import { z } from 'zod'
149
157
 
150
- const schema = z.object({
158
+ const schema = z.object({
151
159
  firstName: z.string(),
152
160
  lastName: z.string()
153
- })
161
+ })
154
162
 
155
- const { VvForm, VvFormWrapper, VvFormField } = useForm(schema, {
163
+ const { VvForm, VvFormWrapper, VvFormField } = useForm(schema, {
156
164
  // lazyLoad: boolean - default false
157
165
  // updateThrottle: number - default 500
158
- // continuosValidation: boolean - default false
166
+ // continuousValidation: boolean - default false
159
167
  // sideEffects?: (formData: any) => void
160
- })
168
+ // scope?: string
169
+ // class?: new (data?: any) => Type
170
+ })
161
171
  </script>
162
172
 
163
173
  <template>
164
- <VvForm>
165
- <VvFormField type="text" name="firstName" label="First Name" />
166
- <VvFormField type="text" name="lastName" label="Last Name" />
167
- </VvForm>
174
+ <VvForm>
175
+ <VvFormField type="text" name="firstName" label="First Name" />
176
+ <VvFormField type="text" name="lastName" label="Last Name" />
177
+ </VvForm>
168
178
  </template>
169
179
  ```
170
180
 
@@ -177,30 +187,34 @@ import { useForm } from '@volverjs/form-vue'
177
187
  import { z } from 'zod'
178
188
 
179
189
  const schema = z.object({
180
- name: z.string(),
181
- surname: z.string()
190
+ firstName: z.string(),
191
+ lastName: z.string()
182
192
  })
183
193
 
184
194
  const {
185
- VvForm,
186
- VvFormWrapper,
187
- VvFormField,
188
- VvFormTemplate,
189
- formData,
190
- status,
191
- errors
195
+ VvForm,
196
+ VvFormWrapper,
197
+ VvFormField,
198
+ VvFormFieldsGroup,
199
+ VvFormTemplate,
200
+ formData,
201
+ status,
202
+ errors,
203
+ wrappers
192
204
  } = useForm(schema, {
193
- lazyLoad: true
205
+ lazyLoad: true
194
206
  })
195
207
 
196
208
  export default {
197
- VvForm,
198
- VvFormWrapper,
199
- VvFormField,
200
- VvFormTemplate,
201
- formData,
202
- status,
203
- errors
209
+ VvForm,
210
+ VvFormWrapper,
211
+ VvFormField,
212
+ VvFormFieldsGroup,
213
+ VvFormTemplate,
214
+ formData,
215
+ status,
216
+ errors,
217
+ wrappers
204
218
  }
205
219
  ```
206
220
 
@@ -211,20 +225,20 @@ The wrapper status is invalid if at least one of the fields inside it is invalid
211
225
 
212
226
  ```vue
213
227
  <template>
214
- <VvForm>
215
- <VvFormWrapper v-slot="{ invalid }">
216
- <div class="form-section-1">
217
- <span v-if="invalid">There is a validation error</span>
218
- <!-- form fields of section 1 -->
219
- </div>
220
- </VvFormWrapper>
221
- <VvFormWrapper v-slot="{ invalid }">
222
- <div class="form-section-2">
223
- <span v-if="invalid">There is a validation error</span>
224
- <!-- form fields of the section 2 -->
225
- </div>
226
- </VvFormWrapper>
227
- </VvForm>
228
+ <VvForm>
229
+ <VvFormWrapper v-slot="{ invalid }" name="firstSection">
230
+ <div class="form-section-1">
231
+ <span v-if="invalid">There is a validation error</span>
232
+ <!-- form fields of section 1 -->
233
+ </div>
234
+ </VvFormWrapper>
235
+ <VvFormWrapper v-slot="{ invalid }" name="secondSection">
236
+ <div class="form-section-2">
237
+ <span v-if="invalid">There is a validation error</span>
238
+ <!-- form fields of the section 2 -->
239
+ </div>
240
+ </VvFormWrapper>
241
+ </VvForm>
228
242
  </template>
229
243
  ```
230
244
 
@@ -232,23 +246,37 @@ The wrapper status is invalid if at least one of the fields inside it is invalid
232
246
 
233
247
  ```vue
234
248
  <template>
235
- <VvForm>
236
- <VvFormWrapper v-slot="{ invalid }">
237
- <div class="form-section">
238
- <span v-if="invalid">There is a validation error</span>
239
- <!-- form fields of section -->
240
- <VvFormWrapper v-slot="{ invalid: groupInvalid }">
241
- <div class="form-section__group">
242
- <span v-if="groupInvalid">There is a validation error</span>
243
- <!-- form fields of the group -->
244
- </div>
249
+ <VvForm>
250
+ <!-- main VvFormWrapper -->
251
+ <VvFormWrapper v-slot="{ invalid }" name="firstSection">
252
+ <!-- add VvFormFields to wrapper -->
253
+ <div class="form-section">
254
+ <span v-if="invalid">There is a validation error</span>
255
+ <!-- nested VvFormWrapper -->
256
+ <VvFormWrapper v-slot="{ invalid: groupInvalid }">
257
+ <div class="form-section__group">
258
+ <span v-if="groupInvalid">There is a validation error</span>
259
+ <!-- add VvFormFields to nested wrapper -->
260
+ </div>
261
+ </VvFormWrapper>
262
+ </div>
245
263
  </VvFormWrapper>
246
- </div>
247
- </VvFormWrapper>
248
- </VvForm>
264
+ </VvForm>
249
265
  </template>
250
266
  ```
251
267
 
268
+ The `wrappers` map provides access to form wrapper data.
269
+ This allows for better control over form validation state and data management.
270
+
271
+ ```vue
272
+ <script setup>
273
+ const { wrappers } = useForm(schema)
274
+
275
+ // Access wrapper data
276
+ const isFirstSectionInvalid = computed(() => wrappers.get('firstSection').invalid)
277
+ </script>
278
+ ```
279
+
252
280
  ### VvFormField
253
281
 
254
282
  `VvFormField` allow you to render a form field or a [`@volverjs/ui-vue`](https://github.com/volverjs/ui-vue) input component inside a form.
@@ -257,26 +285,26 @@ It automatically bind the form data through the `name` attribute. For nested obj
257
285
 
258
286
  ```vue
259
287
  <template>
260
- <VvForm>
261
- <VvFormField
262
- v-slot="{ modelValue, invalid, invalidLabel, onUpdate }"
263
- name="lastName"
264
- >
265
- <label for="lastName">Last Name</label>
266
- <input
267
- id="lastName"
268
- type="text"
269
- name="lastName"
270
- :value="modelValue"
271
- :aria-invalid="invalid"
272
- :aria-errormessage="invalid ? 'last-name-alert' : undefined"
273
- @input="onUpdate"
274
- />
275
- <small v-if="invalid" role="alert" id="last-name-alert">
276
- {{ invalidLabel }}
277
- </small>
278
- </VvFormField>
279
- </VvForm>
288
+ <VvForm>
289
+ <VvFormField
290
+ v-slot="{ modelValue, invalid, invalidLabel, onUpdate }"
291
+ name="lastName"
292
+ >
293
+ <label for="lastName">Last Name</label>
294
+ <input
295
+ id="lastName"
296
+ type="text"
297
+ name="lastName"
298
+ :value="modelValue"
299
+ :aria-invalid="invalid"
300
+ :aria-errormessage="invalid ? 'last-name-alert' : undefined"
301
+ @input="onUpdate"
302
+ >
303
+ <small v-if="invalid" id="last-name-alert" role="alert">
304
+ {{ invalidLabel }}
305
+ </small>
306
+ </VvFormField>
307
+ </VvForm>
280
308
  </template>
281
309
  ```
282
310
 
@@ -285,83 +313,218 @@ By default UI components must be installed globally, they can be lazy-loaded wit
285
313
 
286
314
  ```vue
287
315
  <template>
288
- <VvForm>
289
- <VvFormField type="text" name="username" label="Username" lazy-load />
290
- <VvFormField type="password" name="password" label="Password" lazy-load />
291
- </VvForm>
316
+ <VvForm>
317
+ <VvFormField type="text" name="username" label="Username" lazy-load />
318
+ <VvFormField type="password" name="password" label="Password" lazy-load />
319
+ </VvForm>
292
320
  </template>
293
321
  ```
294
322
 
295
323
  Check the [`VvFormField` documentation](./docs/VvFormField.md) to learn more about form fields.
296
324
 
325
+ ### VvFormFieldsGroup
326
+
327
+ `VvFormFieldsGroup` allow you to render a group of form fields inside a form.
328
+
329
+ It automatically bind the form data through the `names` attribute. For nested objects, use the `names` attribute with **dot notation**.
330
+
331
+ ```vue
332
+ <template>
333
+ <VvForm>
334
+ <VvFormFieldsGroup
335
+ v-slot="{ modelValue, invalids, invalidLabels, onUpdateField }"
336
+ :names="['firstName', 'lastName']"
337
+ >
338
+ <fieldset>
339
+ <p>
340
+ <label for="firstName">First Name</label>
341
+ <input
342
+ id="firstName"
343
+ type="text"
344
+ name="firstName"
345
+ :value="modelValue.firstName"
346
+ :aria-invalid="invalids.firstName"
347
+ :aria-errormessage="invalids.firstName ? 'first-name-alert' : undefined"
348
+ @input="onUpdateField('firstName', $event)"
349
+ >
350
+ <small v-if="invalids.firstName" id="first-name-alert" role="alert">
351
+ {{ invalidLabels?.firstName }}
352
+ </small>
353
+ </p>
354
+ <p>
355
+ <label for="lastName">Last Name</label>
356
+ <input
357
+ id="lastName"
358
+ type="text"
359
+ name="lastName"
360
+ :value="modelValue.lastName"
361
+ :aria-invalid="invalids.lastName"
362
+ :aria-errormessage="invalids.lastName ? 'last-name-alert' : undefined"
363
+ @input="onUpdateField('lastName', $event)"
364
+ >
365
+ <small v-if="invalids.lastName" id="last-name-alert" role="alert">
366
+ {{ invalidLabels?.lastName }}
367
+ </small>
368
+ </p>
369
+ </fieldset>
370
+ </VvFormFieldsGroup>
371
+ </VvForm>
372
+ </template>
373
+ ```
374
+
375
+ Alternatively, you can create a custom component to render the group of form fields.
376
+
377
+ ```vue
378
+ // MyFieldsGroup.vue
379
+ <script lang="ts" setup>
380
+ defineProps<{
381
+ invalids: Record<string, boolean>
382
+ invalidLabels?: Record<string, string[]>
383
+ }>()
384
+ // v-model:first-name
385
+ const firstName = defineModel<string>('firstName', { default: '' })
386
+ // v-model:last-name
387
+ const lastName = defineModel<string>('lastName', { default: '' })
388
+ </script>
389
+
390
+ <template>
391
+ <fieldset>
392
+ <p>
393
+ <label for="firstName">First Name</label>
394
+ <input
395
+ id="firstName"
396
+ v-model="firstName"
397
+ type="text"
398
+ name="firstName"
399
+ :aria-invalid="invalids.firstName"
400
+ :aria-errormessage="invalids.firstName ? 'first-name-alert' : undefined"
401
+ >
402
+ <small v-if="invalids.firstName" id="first-name-alert" role="alert">
403
+ {{ invalidLabels?.firstName }}
404
+ </small>
405
+ </p>
406
+ <p>
407
+ <label for="lastName">Last Name</label>
408
+ <input
409
+ id="lastName"
410
+ v-model="lastName"
411
+ type="text"
412
+ name="lastName"
413
+ :aria-invalid="invalids.lastName"
414
+ :aria-errormessage="invalids.lastName ? 'last-name-alert' : undefined"
415
+ >
416
+ <small v-if="invalids.lastName" id="last-name-alert" role="alert">
417
+ {{ invalidLabels?.lastName }}
418
+ </small>
419
+ </p>
420
+ </fieldset>
421
+ </template>
422
+ ```
423
+
424
+ An than use it inside the `VvFormFieldsGroup` with the `:is` attribute.
425
+
426
+ ```vue
427
+ <script>
428
+ import MyFieldsGroup from './MyFieldsGroup.vue'
429
+ </script>
430
+
431
+ <template>
432
+ <VvForm>
433
+ <VvFormFieldsGroup
434
+ :is="MyFieldsGroup"
435
+ :names="['firstName', 'lastName']"
436
+ />
437
+ </VvForm>
438
+ </template>
439
+ ```
440
+
441
+ You can also map the form fields to the components v-models. The `:names` attribute can be an object with the component v-models as keys and the form fields names as values.
442
+
443
+ ```vue
444
+ <script>
445
+ import MyCustomComponent from './MyCustomComponent.vue'
446
+ </script>
447
+
448
+ <template>
449
+ <VvForm>
450
+ <VvFormFieldsGroup
451
+ :is="MyCustomComponent"
452
+ :names="{
453
+ myCustomComponentVModel: 'path.to.form.field',
454
+ }"
455
+ />
456
+ </VvForm>
457
+ </template>
458
+ ```
459
+
297
460
  ## VvFormTemplate
298
461
 
299
462
  Forms can also be created using a template. A template is an **array of objects** that describes the form fields. All properties that are **not listed** below are passed to the component **as props**.
300
463
 
301
464
  ```vue
302
465
  <script lang="ts" setup>
303
- import { useForm } from '@volverjs/form-vue'
304
- import { z } from 'zod'
466
+ import { useForm } from '@volverjs/form-vue'
467
+ import { z } from 'zod'
305
468
 
306
- const schema = z.object({
469
+ const schema = z.object({
307
470
  firstName: z.string(),
308
471
  lastName: z.string(),
309
472
  address: z.object({
310
- street: z.string(),
311
- number: z.string(),
312
- city: z.string(),
313
- zip: z.number()
473
+ street: z.string(),
474
+ number: z.string(),
475
+ city: z.string(),
476
+ zip: z.number()
314
477
  })
315
- })
478
+ })
316
479
 
317
- const templateSchema = [
480
+ const templateSchema = [
318
481
  {
319
- vvName: 'firstName',
320
- vvType: 'text',
321
- label: 'First Name'
482
+ vvName: 'firstName',
483
+ vvType: 'text',
484
+ label: 'First Name'
322
485
  },
323
486
  {
324
- vvName: 'lastName',
325
- vvType: 'text',
326
- label: 'Last Name'
487
+ vvName: 'lastName',
488
+ vvType: 'text',
489
+ label: 'Last Name'
327
490
  },
328
491
  {
329
- vvIs: 'div',
330
- class: 'grid grid-col-3 gap-4',
331
- vvChildren: [
332
- {
333
- vvName: 'address.street',
334
- vvType: 'text',
335
- label: 'Street',
336
- class: 'col-span-2'
337
- },
338
- {
339
- vvName: 'address.number',
340
- vvType: 'text',
341
- label: 'Number'
342
- },
343
- {
344
- vvName: 'address.city',
345
- vvType: 'text',
346
- label: 'City'
347
- class: 'col-span-2',
348
- },
349
- {
350
- vvName: 'address.zip',
351
- vvType: 'number',
352
- label: 'Zip'
353
- }
354
- ]
492
+ vvIs: 'div',
493
+ class: 'grid grid-col-3 gap-4',
494
+ vvChildren: [
495
+ {
496
+ vvName: 'address.street',
497
+ vvType: 'text',
498
+ label: 'Street',
499
+ class: 'col-span-2'
500
+ },
501
+ {
502
+ vvName: 'address.number',
503
+ vvType: 'text',
504
+ label: 'Number'
505
+ },
506
+ {
507
+ vvName: 'address.city',
508
+ vvType: 'text',
509
+ label: 'City',
510
+ class: 'col-span-2',
511
+ },
512
+ {
513
+ vvName: 'address.zip',
514
+ vvType: 'number',
515
+ label: 'Zip'
516
+ }
517
+ ]
355
518
  }
356
- ]
519
+ ]
357
520
 
358
- const { VvForm, VvFormTemplate } = useForm(schema)
521
+ const { VvForm, VvFormTemplate } = useForm(schema)
359
522
  </script>
360
523
 
361
524
  <template>
362
- <VvForm>
363
- <VvFormTemplate :schema="templateSchema" />
364
- </VvForm>
525
+ <VvForm>
526
+ <VvFormTemplate :schema="templateSchema" />
527
+ </VvForm>
365
528
  </template>
366
529
  ```
367
530
 
@@ -378,69 +541,69 @@ Conditional rendering can be achieved using the `vvIf` and `vvElseIf` properties
378
541
 
379
542
  ```vue
380
543
  <script lang="ts" setup>
381
- import { useForm } from '@volverjs/form-vue'
382
- import { z } from 'zod'
544
+ import { useForm } from '@volverjs/form-vue'
545
+ import { z } from 'zod'
383
546
 
384
- const schema = z.object({
547
+ const schema = z.object({
385
548
  firstName: z.string(),
386
549
  lastName: z.string(),
387
550
  hasUsername: z.boolean(),
388
- username: z.string().optional()
551
+ username: z.string().optional(),
389
552
  email: z.string().email().optional()
390
- }).superRefine((value, ctx) => {
553
+ }).superRefine((value, ctx) => {
391
554
  if (value.hasUsername && !value.username) {
392
- ctx.addIssue({
393
- code: z.ZodIssueCode.custom,
394
- message: 'Username is required'
395
- })
555
+ ctx.addIssue({
556
+ code: z.ZodIssueCode.custom,
557
+ message: 'Username is required'
558
+ })
396
559
  }
397
560
  if (!value.hasUsername && !value.email) {
398
- ctx.addIssue({
399
- code: z.ZodIssueCode.custom,
400
- message: 'Email is required'
401
- })
561
+ ctx.addIssue({
562
+ code: z.ZodIssueCode.custom,
563
+ message: 'Email is required'
564
+ })
402
565
  }
403
- })
566
+ })
404
567
 
405
- const templateSchema = [
568
+ const templateSchema = [
406
569
  {
407
- vvName: 'firstName',
408
- vvType: 'text',
409
- label: 'First Name'
570
+ vvName: 'firstName',
571
+ vvType: 'text',
572
+ label: 'First Name'
410
573
  },
411
574
  {
412
- vvName: 'lastName',
413
- vvType: 'text',
414
- label: 'Last Name'
575
+ vvName: 'lastName',
576
+ vvType: 'text',
577
+ label: 'Last Name'
415
578
  },
416
579
  {
417
- vvName: 'hasUsername',
418
- vvType: 'checkbox',
419
- label: 'Has Username'
420
- value: true,
421
- uncheckedValue: false
580
+ vvName: 'hasUsername',
581
+ vvType: 'checkbox',
582
+ label: 'Has Username',
583
+ value: true,
584
+ uncheckedValue: false
422
585
  },
423
586
  {
424
- vvIf: 'hasUsername',
425
- vvName: 'username',
426
- vvType: 'text',
427
- label: 'Username'
587
+ vvIf: 'hasUsername',
588
+ vvName: 'username',
589
+ vvType: 'text',
590
+ label: 'Username'
428
591
  },
429
592
  {
430
- vvElseIf: true,
431
- vvName: 'email',
432
- vvType: 'email',
433
- label: 'Email'
593
+ vvElseIf: true,
594
+ vvName: 'email',
595
+ vvType: 'email',
596
+ label: 'Email'
434
597
  }
435
- ]
598
+ ]
436
599
 
437
- const { VvForm, VvFormTemplate } = useForm(schema)
600
+ const { VvForm, VvFormTemplate } = useForm(schema)
438
601
  </script>
439
602
 
440
603
  <template>
441
- <VvForm>
442
- <VvFormTemplate :schema="templateSchema" />
443
- </VvForm>
604
+ <VvForm>
605
+ <VvFormTemplate :schema="templateSchema" />
606
+ </VvForm>
444
607
  </template>
445
608
  ```
446
609
 
@@ -449,39 +612,43 @@ Conditional rendering can be achieved using the `vvIf` and `vvElseIf` properties
449
612
  `vvIf` and `vvElseIf` can be a string or a function. If it is a string it will be evaluated as a **property** of the form data. If it is a function it will be called with the **form context** as the **first argument** and must return a boolean.
450
613
 
451
614
  ```ts
452
- {
453
- vvIf: (ctx) => ctx.formData.value.hasUsername,
454
- vvName: 'username',
455
- vvType: 'text',
456
- label: 'Username'
457
- }
615
+ const templateSchema = [
616
+ {
617
+ vvIf: ctx => ctx.formData.value.hasUsername,
618
+ vvName: 'username',
619
+ vvType: 'text',
620
+ label: 'Username'
621
+ }
622
+ ]
458
623
  ```
459
624
 
460
625
  Also the template schema and all template items can be a function.
461
626
  The function will be called with the **form context** as the **first argument**.
462
627
 
463
628
  ```ts
464
- const templateSchema = (ctx) => [
465
- {
466
- vvName: 'firstName',
467
- vvType: 'text',
468
- label: `Hi ${ctx.formData.value.firstName}!`
469
- }
470
- ]
629
+ function templateSchema(ctx) {
630
+ return [
631
+ {
632
+ vvName: 'firstName',
633
+ vvType: 'text',
634
+ label: `Hi ${ctx.formData.value.firstName}!`
635
+ }
636
+ ]
637
+ }
471
638
  ```
472
639
 
473
640
  ```ts
474
641
  const templateSchema = [
475
- (ctx) => ({
476
- vvName: 'firstName',
477
- vvType: 'text',
478
- label: `Hi ${ctx.formData.value.firstName}!`
479
- }),
480
- {
481
- vvName: 'username',
482
- type: 'text',
483
- label: 'username'
484
- }
642
+ ctx => ({
643
+ vvName: 'firstName',
644
+ vvType: 'text',
645
+ label: `Hi ${ctx.formData.value.firstName}!`
646
+ }),
647
+ {
648
+ vvName: 'username',
649
+ type: 'text',
650
+ label: 'username'
651
+ }
485
652
  ]
486
653
  ```
487
654
 
@@ -495,15 +662,15 @@ import { z } from 'zod'
495
662
  import { defaultObjectBySchema } from '@volverjs/form-vue'
496
663
 
497
664
  const schema = z.object({
498
- name: z.string().default('John'),
499
- surname: z.string().default('Doe')
665
+ firstName: z.string().default('John'),
666
+ lastName: z.string().default('Doe')
500
667
  })
501
668
 
502
669
  const defaultObject = defaultObjectBySchema(schema)
503
- // defaultObject = { name: 'John', surname: 'Doe' }
670
+ // defaultObject = { firstName: 'John', lastName: 'Doe' }
504
671
 
505
672
  const defaultObject = defaultObjectBySchema(schema, { name: 'Jane' })
506
- // defaultObject = { name: 'Jane', surname: 'Doe' }
673
+ // defaultObject = { firstName: 'Jane', lastName: 'Doe' }
507
674
  ```
508
675
 
509
676
  `defaultObjectBySchema` can be used with nested objects.
@@ -513,16 +680,16 @@ import { z } from 'zod'
513
680
  import { defaultObjectBySchema } from '@volverjs/form-vue'
514
681
 
515
682
  const schema = z.object({
516
- name: z.string().default('John'),
517
- surname: z.string().default('Doe'),
518
- address: z.object({
519
- street: z.string().default('Main Street'),
520
- number: z.number().default(1)
521
- })
683
+ firstName: z.string().default('John'),
684
+ lastName: z.string().default('Doe'),
685
+ address: z.object({
686
+ street: z.string().default('Main Street'),
687
+ number: z.number().default(1)
688
+ })
522
689
  })
523
690
 
524
691
  const defaultObject = defaultObjectBySchema(schema)
525
- // defaultObject = { name: 'John', surname: 'Doe', address: { street: 'Main Street', number: 1 } }
692
+ // defaultObject = { firstName: 'John', lastName: 'Doe', address: { street: 'Main Street', number: 1 } }
526
693
  ```
527
694
 
528
695
  Other Zod methods are also supported: [`z.nullable()`](https://github.com/colinhacks/zod#nullable), [`z.coerce`](https://github.com/colinhacks/zod#coercion-for-primitives) and [`z.passthrough()`](https://github.com/colinhacks/zod#passthrough).
@@ -532,24 +699,24 @@ import { z } from 'zod'
532
699
  import { defaultObjectBySchema } from '@volverjs/form-vue'
533
700
 
534
701
  const schema = z
535
- .object({
536
- name: z.string().default('John'),
537
- surname: z.string().default('Doe'),
538
- address: z.object({
539
- street: z.string().default('Main Street'),
540
- number: z.number().default(1)
541
- }),
542
- age: z.number().nullable().default(null),
543
- height: z.coerce.number().default(1.8),
544
- weight: z.number().default(80)
545
- })
546
- .passthrough()
702
+ .object({
703
+ firstName: z.string().default('John'),
704
+ lastName: z.string().default('Doe'),
705
+ address: z.object({
706
+ street: z.string().default('Main Street'),
707
+ number: z.number().default(1)
708
+ }),
709
+ age: z.number().nullable().default(null),
710
+ height: z.coerce.number().default(1.8),
711
+ weight: z.number().default(80)
712
+ })
713
+ .passthrough()
547
714
 
548
715
  const defaultObject = defaultObjectBySchema(schema, {
549
- height: '1.9',
550
- email: 'john.doe@test.com'
716
+ height: '1.9',
717
+ email: 'john.doe@test.com'
551
718
  })
552
- // defaultObject = { name: 'John', surname: 'Doe', address: { street: 'Main Street', number: 1 }, age: null, height: 1.9, weight: 80, email: 'john.doe@test.com' }
719
+ // defaultObject = { firstName: 'John', lastName: 'Doe', address: { street: 'Main Street', number: 1 }, age: null, height: 1.9, weight: 80, email: 'john.doe@test.com' }
553
720
  ```
554
721
 
555
722
  ## License