Binding the Vue 3 v-model to an input component that is nested within another component

As I work on migrating my components to Vue 3, I'm facing confusion with setting the V-model, particularly in a nested component like CheckboxFieldComponent. The initial state of the checkbox should be unchecked as I retrieve a value from the API. Where would be the best place to set the v-model for this nested component?

CheckboxFieldComponent.vue

<template>
    <div v-show="isVisible">
      <FormCheckbox
        v-model="booleanFromStoreConvertedToArray"
        :input-value="field.model.itemId"
        :title="title"
        :id="field.valueField.id"
        :class="field.model.cssClass"
        :name="field.valueField.name"
        :checked="true"
        :required="required"
        :disabled="!isEnabled"
        @input="
          onInput(value);
          $emit('change');
        "
        @focus="field.tracker.onFocusField(field, value)"
        @blur="field.tracker.onBlurField(field, value, errors.value)"
      />
    </div>
</template>

<script>
export default {
  extends: BaseField,
  computed: {
    booleanFromStoreConvertedToArray: {
      get() {
        const isSelected = this.value;
        if (isSelected) {
          return [this.field.model.itemId];
        }
        return [];
      },
      set(value) {
        this.value = value.length > 0;
      },
    },
  },
};
</script>

FormCheckbox.vue
<template>
  <FormCheckboxBase
    :checked="shouldBeChecked"
    type="checkbox"
    :class="$style.check"
    :input-value="inputValue"
    :title="title"
    :id="id"
    :name="name"
    :required="required"
    v-bind="$attrs"
    @updateInput="updateInput"
  />
</template>

<script>
export default {
  components: {
    FormCheckboxBase,
  },
  props: {
    value: {
      type: Array,
      default: () => [],
    },
    inputValue: {
      // this is the value that will be assigned to the html element's value-property
      type: String,
      required: true,
    },
    title: {
      type: String,
      required: true,
    },
    id: {
      type: String,
      required: true,
    },
    name: {
      type: String,
      default: '',
    },
    required: {
      type: Boolean,
      default: false,
    },
  },
  computed: {
    shouldBeChecked() {
      if (!this.value) {
        return false;
      }
      return this.value.includes(this.inputValue);
    },
  },
  methods: {
    updateInput(event) {
      const { checked, value } = event.target;
      const newValue = [...this.value];
      let checkLabel;

      if (checked) {
        checkLabel = 'on';
        newValue.push(value);
      } else {
        checkLabel = 'off';
        newValue.splice(newValue.indexOf(value), 1);
      }

      // this.$emit('input', newValue); <--- This is from Vue 2 which was working
    },
  },
};
</script>

FormCheckboxBase.vue

<template>
  <div :class="$style['form-item']">
    <input
      :id="id"
      :value="inputValue"
      @change="updateInput"
      :type="type"
      v-bind="$attrs"
      class="input"
      :class="$style.input"
    />
    <slot />
  </div>
</template>

<script>
export default {
  props: {
    type: {
      type: String,
      default: 'checkbox',
    },
    inputValue: {
      type: String,
      required: true,
    },
    title: {
      type: String,
      required: true,
    },
    id: {
      type: String,
      required: true,
    },
    required: {
      type: Boolean,
      default: false,
    },
    checkboxList: {
      type: Boolean,
      default: false,
    },
  },
  methods: {
    updateInput(event) {
      // this.$emit('updateInput', event); <--- This is From Vue2 Event Emitter
    },
  },
};
</script>

In order to address the issue of triggering the onInput event twice and not updating the value when interacting with the field, I leveraged the BaseField component extension to ensure proper functionality.

BaseField.vue

onInput(value) {
   this.value = value;
   // validate
   this.validate();
},

Answer №1

Implementing v-model on a component

The official documentation states that using v-model on a component can be achieved by expanding it to:

<CustomInput
  :model-value="searchText"
  @update:model-value="newValue => searchText = newValue"
/>

For setting v-model on a FormCheckbox, the modelValue prop must be set to establish binding, and emitting update:modelValue enables two-way binding.

To apply these changes in FormCheckbox.vue:

props: {
  modelValue: {
    type: Array,
    default: () => [],
  },
  ...
}

Issue with nested checkbox bindings

In FormCheckboxBase.vue, when the <input> type is checkbox, v-bind:value will not affect checked. Instead, use v-bind:check in FormCheckboxBase.vue:

<template>
  <div>
    <input
      :id="id"
      :type="type"
      :checked="checked"
      :required="required"
      :value="inputValue"
      v-bind="$attrs"
      @change="updateInput"
    />
  <slot />
  </div>
</template>

Duplicated event triggering explanation

Setting @input on FormCheckbox triggers upon checkbox checked change. However, this.value in CheckboxFieldComponent.vue does not update until @change triggers (onInput before onChanged).

Additionally, this.value is updated by the setter of booleanFromStoreConvertedToArray, establishing two-way binding via v-model.

Ensure validation within the setter of booleanFromStoreConvertedToArray for proper functionality:

booleanFromStoreConvertedToArray: {
  get() {
    const isSelected = this.value;
    if (isSelected) {
      return [this.field.model.itemId];
    }
    return [];
  },
  set(value) {
    this.value = value.length > 0;
    // Add any required post-update logic here
    this.validate();
  }
}

Find the demo link here: https://codesandbox.io/s/question-77194739-f9wm4g

Trust this information proves helpful!

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Forming a multidimensional array based on the values extracted from a JSON file

I am struggling with creating a for loop to return an array from a JSON file named data.json. Here is the desired array structure: var arr=[["CR7012","",100,1,100],[CR7012,"bla bla bla",100,5,500],["CR7012,"bla bla bla",100,1,100]] The content of dat ...

The warning message "UnhandledPromiseRejectionWarning: Error: ENOENT: unable to locate the specified file or directory" was triggered due to the absence of the file "level

Currently, I am working on a project involving a popular application called Discord. My main task is to code a bot that can send images from a local file source. The bot is hosted on Heroku, which means all the files are stored in the cloud and maintained ...

Is it possible for a Vue Boolean prop to be true if present and false if absent?

Within my Vue component, I've implemented a Boolean prop named "obj" with the following definition: obj: { Type:Boolean, default: false} To set it to true, one can simply do as follows: <my-component :obj="true"></my-component> Yet, I ...

Responding to a specific user ID with DiscordJS

I am facing an issue where I want the bot to reply only to a specific user. I tried implementing this code, but it doesn't seem to work as expected. Could you assist me in resolving this problem? const userID = '322032528460611604' client.o ...

The container is not showing the JSTree as expected

My current project in JavaScript involves integrating a JSTree structure, but I'm encountering an issue where the tree is not showing up or rendering within its specified parent container. Below is the snippet of code I have been using to attempt to d ...

Using AngularJs to have two ui-views on a single page

As a beginner in AngularJs, I encountered an issue with having two ui-views on the same page. When I click a button to show the view for goals, both the form for goals appear in ui-view 1 and ui-view 2 simultaneously. I have been working with states but I ...

AngularJS - where each client maintains their own MongoDB database

After spending hours on this task, I'm still struggling to find a solution. I have a mean stack application that caters to multiple clients, and I am looking to assign each client their own database. Is this possible? For Example var client_name = ...

Guide to integrating Google Maps into your Vue application using vue2-google-maps

I have been trying to integrate vue2-google-maps into my app, but unfortunately, the map is not displaying as expected. Despite carefully following the documentation on how to use it, all I get is a blank white page instead of the Google Maps interface. St ...

Can styles be added using script code?

A kind member from this site has assisted me in fixing a script. This initial segment of the code allows for the categorization and separation of blog articles by tags. Is it feasible to incorporate CSS into this section of the code, where the tags Terro ...

Retrieve and search for the URL of the link being hovered over exclusively

As indicated by the title 「 Obtain and search for the URL of the hovered link "only" 」 What corrections should be made to accomplish this? // Fix1⃣ Issue // ① Opens all URLs that contain the specified word, regardless of mouseover target. // ② ...

The error encountered at line 8 in the file ui-bootstrap-tpls-0.14.3.min.js is: "Uncaught ReferenceError:

Everything was working perfectly in my application until I decided to install ui-bootstrap-tpls-0.14.3.min.js I added ui.bootstrap as a dependency like this: var app = angular.module("earnfitApp", ['ngRoute','ui.bootstrap']); I made ...

What is the best way to change a date from the format DD/MM/YYYY to YYYY-MM-DD

Is there a way to use regular expressions (regex) to convert a date string from DD/MM/YYYY format to YYYY-MM-DD format? ...

onChange does not trigger useEffect

In the Order Content component, I am receiving some products as props to be used in a select component. When a product is selected in the dropdown, it triggers the rendering of Summary and Product components. In these components, the user can choose the ...

Issue with Post request from fetch causing a server error (javascript/express)

Currently, I am experimenting to determine if a POST request will function correctly on my server through the '/db' route. Surprisingly, while the GET request is successful, the POST request consistently results in a '404 - not found' e ...

The React-native application initialized using npx create-react-app encountered an error and could not launch

Hello there, A couple of months back, I developed an app using create-react-app. However, when I tried to run it with npm start, I encountered the following error message: react-scripts start It seems like there's an issue with the project depende ...

Rearrange and place items at the dropped location

I am looking to create a drag-and-drop feature for moving items from a list of products to an empty container. Upon completion, I need to save the location and the selected items in a database so that I can recall this layout later. However, I have encoun ...

The new pop-up window appears smaller than expected in Internet Explorer

There has been a bug report regarding the course opening in a smaller popup window. The JavaScript code used to open the popup is: course_window=window.open(urlString,"", "toolbar=0,directories=0,location=0,status=0, menubar=0,fullscreen=0,scroll ...

What is the best way to shift a three.js model to the right side of the screen?

Recently, I've delved into learning three.js and I'm curious to know if it's possible to shift a 3D model to the right side of the screen instead of having it centered. To better illustrate my query, I've included an image below: http ...

Using JQuery to assign a value to a hidden input field

How do I add value to a hidden input field? In this scenario, the input is created as a variable and later inserted into the DOM if necessary. Here is the code snippet: <script> var form = $('form#small_ad_form'), help = $('di ...

Combining intersecting objects with interval attributes in an array using javascript

I have encountered similar questions, however, the provided answers do not resolve my specific issue. The data I have is an array of range objects, each with the following properties: start: Integer, indicating the start of the range, end: Integer, indi ...