2

I'm currently upgrading a Laravel Nova project from Nova 3 (which uses Vue2) to Nova 4 (which uses Vue3). And for that reason, we had to do some modification in our custom components.

Like, in Vue3 we have used different method other than $parent and $children to fetch the child components of parent compoent. This change worked perfectly.

But, in Vue2 there is $watch property that used to watch any changes in the child component. And to replicate same functionality in Vue3, I tried several methods, but I am not able to find a solution for it. You can see the difference in below components.

Below are the snippets from both Vue2 and Vue3 components.

Vue2 Component (Nova 3):

<template>
  <div  class="-mx-8 px-8">
    <div v-for="condition in field.conditions" :key="condition.value">
      <div v-if="condition.value == value">
        <component
          v-bind="$props"
          :key="subfield"
          :field="subfield"
          :is="'form-' + subfield.component"
          v-for="subfield in condition.fields"
        />
        <div class="help-text error-text mt-2 text-danger" v-if="hasError">
          {{ firstError }}
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import { FormField } from 'laravel-nova'
import { Errors } from 'form-backend-validation'

export default {
  mixins: [FormField],

  props: {
    errors: {
      default: () => new Errors(),
    },
    resource: {
      required: true
    },
    resourceId: {
      required: true
    },
    'base-classes': {
      type: Object
    }
  },

  data() {
    return {
      value: null,
      errorClass: 'border-danger',
    }
  },

  mounted() {
    this.children.forEach(component => {
      if(component.field !== undefined && component.field.attribute === this.field.observed_field_name) {
        component.$watch(this.field.customProperty || 'value', (value) => {
          this.value = value
        }, {deep: true, immediate: true});
      }
    });
  },

  computed: {
    parent() {
      let parent = this.$parent

      for (let i = 1; i < (this.field.depth || 1); i++) {
        parent = parent.$parent
      }

      return parent
    },

    children() {
      let children = this.parent.$children

      for (let i = 1; i < (this.field.depth || 1); i++) {
        children = children.filter(c => c.$children.length).map(c => c.$children[0])
      }

      return children
    },
  },
}
</script>

Vue3 Component (Nova 4):

<template>
  <div  class="-mx-8 px-8">
    <div v-for="condition in field.conditions" :key="condition.value">
      <div v-if="condition.value == value">
        <component
          v-bind="$props"
          :key="subfield"
          :field="subfield"
          :is="'form-' + subfield.component"
          v-for="subfield in condition.fields"
        />
        <div class="help-text error-text mt-2 text-danger" v-if="hasError">
          {{ firstError }}
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import { ref, watch, onMounted, getCurrentInstance } from 'vue';
import { Errors } from 'form-backend-validation';

export default {
  props: {
    errors: {
      default: () => new Errors(),
    },
    resource: {
      required: true
    },
    resourceId: {
      required: true
    },
    'base-classes': {
      type: Object
    },
    field: {
      type: Object,
      required: true
    }
  },

  setup(props) {
    const value = ref(null);
    const errorClass = 'border-danger';
    const instance = getCurrentInstance();

    // This needs to be carefully handled, as it relies on internal structure
    const children = () => {
      return instance.parent.subTree.children;
    };

    onMounted(() => {
      children().forEach(component => {
        let child = component.children[0];
        if (child.props.field && child.props.field.attribute === props.field.observed_field_name) {
          watch(() => child.component, (newValue) => {
            value.value = newValue;
          }, { deep: true, immediate: true });
        }
      });
    });

    // Computed and methods can be added here as needed

    return { value, errorClass };
  }
}
</script>

To explain more the use of $watch in project, below is the screenshot for reference.

enter image description here

enter image description here

So here in the screenshot you can see there are fields. And each field is a Vue component itself. They can be Nova default fields or they can be custom one.

The vue compoent code pasted at the top, is Role field (see in second screenshot) and it is a custom component. The Role field is dependent on the Company field. If Company field is blank then Role field will get hide and it will get displayed when Company field is selected (that too for specific values).

So basically $watch property is used to keep watch on changes in Company field (specific element within Company component - this.field.customProperty). In Vue2 it was easier to handle this as you can see in the Vue2 code. But, in Vue3 I am not finding proper solution or may be I do not know proper code to watch the compoent.

Does any one know how should I watch child component in Vue3?

Please note:

I can not directly access parent or child component to use ref method. Since Laravel Nova does not allow this.

There is new method dependOn in Laravel Nova 4 and by using it I could avoid using this custom component, but I do not have to use it since this custom component is also used for other complex resources(pages) in the project.

0

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.