vee-validate icon indicating copy to clipboard operation
vee-validate copied to clipboard

Migrating v3 to v4, multiple root elements

Open Razi91 opened this issue 3 years ago • 2 comments

Is your feature request related to a problem? Please describe. When migrating from v3 to v4, I've just register component Field as ValidationProvider and used old code. I have many wrappers like:

<template>
  <ValidationProvider
    :rules="rules"
    :name="label"
    :vid="vid"
    v-slot="{ errors, invalid, validated, required }"
    slim
  >
    <q-select/>
  </ValidationProvider>
</template>

But after migration, I can't use custom directives (for tests mostly), Vue informs that directive is not used on element root element.

I found that old version extracted first element from slot:

return this.slim && children.length <= 1 ? children[0] : h(this.tag, children);

While new doesn't do that.

If I add

      if (Array.isArray(children) && children.length === 1) {
        return children[0];
      }
      return children;

it works, I don't get the warning in console and my data-cy="Component.fieldName" is attached. But in sources, the test <Field /> renderless if no as prop and default slot exists fails: select is rendered, but really, I see no reason why it shouldn't be. It's that one:

  test('renderless if no as prop and default slot exists', () => {
    const wrapper = mountWithHoc({
      template: `
      <Field name="field" v-slot="{ field }">
        <select v-bind="field">
          <option>1</option>
        </select>
      </Field>
    `,
    });

    expect(wrapper.$el.tagName).toBe(undefined);
  });

I'm planning to move into composition API completely, but I don't want to do it right now

Razi91 avatar Jan 03 '22 14:01 Razi91

I thought about this before and There is a situation where it becomes ambiguous. Consider this template:

<Field name="field" v-slot="{ field, errorMessage }">
  <select v-bind="field">
    <option>1</option>
  </select>
  <span v-if="errorMessage">{{ errorMessage }}</span>
</Field>

Now that template is effectively equivalent to the one you mentioned when errorMessage is empty. But when it is not it will instead be a renderless component which is probably not expected.

If you want to avoid this I think you can always provide as prop and give the Field a different wrapper which would work with custom directives since it will render a root element then.

I will keep this issue open till I check this behavior again and see if it is still problematic.

logaretm avatar Jan 05 '22 00:01 logaretm

I was considering it, but it would wrap everything in another unnecessary node. But right, this case explains that. But maybe this prop slim would resolve that, it could tell the component there will be just one child, just like in older vee validate version:

      if (props.slim && Array.isArray(children) && children.length === 1) {
        return children[0];
      }

Razi91 avatar Jan 07 '22 08:01 Razi91