jarvelov/vue-form-json-schema

Using the errorHandler to display errors in an array with an embedded vfjs

itgeniq opened this issue · 2 comments

I am having a little bit of trouble figuring out how to get the errorHandler to work when dealing with arrays of data.

I have a schema with an array of repeating fields. I have put together an example based on option 3 from issue #36. Note that my use case will have more complex objects in the array but this example should be enough to show what I am trying to do!

https://codesandbox.io/s/sad-ishizaka-7mhj9

I have modified the inputs to use q-inputs from the quasar framework.

I have setup the firstName field (which is not part of the array) to display the error in the q-input by using the errorHandler. So if I enter a name with less than 4 characters as specified by the minLength then the error prop is set and the error message is being displayed in the error slot. Which is looks fantastic and is exactly what I want.

The problem I have is getting the error messages for the fields in the array to be handled in a similar way by the errorHandler.

So I have 2 questions

  1. Is using the errorHandler the best way to get the error for the field and display as I have shown in the First Name field on the demo?
  2. Is it possible to get the errorHandler to work with the fruiteName fields inside the array?

I can see the ajv errors in the state referencing the dataPath of .fruits[1].fruitName but when I inspect the fields in the vue utils the vfjsFieldErrors is not set. The vfjsFieldErrors only appear to be set on the top level array field.

Note I am new to Vue so may be missing something obvious but is there a way to get this to work?

Hi!

  1. Is using the errorHandler the best way to get the error for the field and display as I have shown in the First Name field on the demo?

What is the best way is hard to say for sure, but it is probably the easiest way for the setup you have. Other ways are to use the error from the vfjsFieldErrors prop. If you want to customize how the errors are displayed, then you should create a component which accepts the vfjsFieldErrors prop. That prop is passed an array of errors for the field.

  1. Is it possible to get the errorHandler to work with the fruiteName fields inside the array?

Yes, with two small changes:

// app-list-item.vue
<vue-form-json-schema
...
:options="vfjsOptions" // Add prop with same name as well
:schema="vfjsFieldSchema.items" // This is important and was missing in example 3 in #36, read more about this below
...
</vue-form-json-schema>

You can see a demo of this working here: https://codesandbox.io/s/strange-saha-93ttm

Now to explain what we just did in app-list.item.vue.

First we bind the options prop to vfjsOptions. vfjsOptions is the prop VFJS uses to pass down its options to the field. Which is the same as the data property options from the example.vue component. If we look at options in example.vue it is set to:

options: {
  showValidationErrors: true, // This tells VFJS to show errors
}

Without showValidationErrors the inner VFJS forms in app-list-item won't display the field's errors. What we just did was to pass along the options all the way from the outermost form in example.vue to the innermost ones in app-list-item.vue.

All right, the second part might not be as easy to explain, but lets start by looking at the value of vfjsFieldSchema we get the following:

{
  type: "array",
  items: {
    type: "object",
    required: ["fruitName"],
    properties: {
      fruitName: {
        type: "string",
        minLength: 4
      }
    }
  }
}

All right, so what's the problem here? We'll get into that shortly, but lets now take a look at what we pass in as the UI Schema, the value of prop vfjsChildrenUiSchema.

{
  component: "q-input",
  model: "fruitName",
  fieldOptions: {
    on: ["input"],
    attrs: {
      ...
    }
  },
  ...
  children: [{
    component: "div",
    errorHandler: true,
    model: "fruitName",
  }]
}

Here we create the fields for fruitName. Looks good, nothing out of the ordinary. Now if we write something into the field and log the output in the onChange function we see the following:

console.log(index, model);
// outputs: 1, { fruitName: 'test' }

Now lets look at the last piece of the puzzle, which is how we use VFJS in the template and then things might become a bit clearer.

<vue-form-json-schema
    v-for="(model, index) in value" // Loop over and create a VFJS component for each item in the array
...
</vue-form-json-schema>

Okay so lets put all the pieces together:

We loop over the value prop and create a VFJS component for each item in the array. The array consists of objects with a fruitName property. Then we pass in the UI Schema which creates fields which uses the fruitName model key. Now, for validation we provide the JSON schema and this is the last piece.

Unfortunately the JSON schema doesn't fit together with the other pieces, and here's why:

When we loop over the model and create a new VFJS component for each item in the array we pass in the following:

model: { fruitName: 'Apple' }
uiSchema: [{ component: 'q-input', model: 'fruitName'}]
schema: { type: 'array', items: { ... } }

Now, the error is right in front of us, we try to use the JSON schema which validates an array, but the model is an object. We want to use the validation for the items in the array, not for the array. So when we look inside the items property we can see that it is what we want to use as the JSON schema:

{
  type: "object",
  required: ["fruitName"],
  properties: {
    fruitName: {
      type: "string",
      minLength: 4
    }
  }
}

Puting it all together we get:

model: { fruitName: 'Apple' }
uiSchema: [{ component: 'q-input', model: 'fruitName'}]
schema: { type: 'object', required: ['firstName'], ... }

Play around with the demo and let me know if you have any more questions.

I suspected it was something simple about the way the details were being passed down from the parent to the child form but I just couldn't figure it out!

I really appreciate the way you broke everything down for me - it's really nice to have it explained in such an easy to understand manner.

I've had a play with the demo and made the changes in my local project and have everything working exactly the way I want it now.

Also, thank you so much for making this library available. It has saved me days, if not weeks, of coding! I had build something similar in a past project using react and jsonschema but not to the level of flexibility of this library!

Thank you so much,

Matt.