Skip to content
Advertisement

Dynamic Vuetify Textbox or Select based on Array Values

I’m working on a modal form which will display a number of v-select or v-text-field… however, the number and types of these elements are being pulled from a database which means the vuetify code to create the elements needs to be dynamic. To start, I have a json object which is returned from the database which describes each input element. If the input is a v-select, then the json property called InputType is set to ‘Combobox’. If it’s a v-text-field then the InputType is set to ‘Textbox’. Here’s a sample.

{
  tags: [
    {
      TagDefinitionId: '1',
      InputType: 'Combobox',
      Name: 'Tag1',
      TagValueOptions: [
        {
          Value: 'Option 1',
        },
        {
          Value: 'Option 2',
        },
        {
          Value: 'Option 3',
        },
      ],
    },
    {
      TagDefinitionId: '2',
      InputType: 'Textbox',
      Name: 'Project Name',
      TagValueOptions: [],
    },
  ],
}

Then, I’m using vuex to map the state in my modal to value called documentTags.

<script>
import { mapState } from 'vuex';

export default {
  name: 'MyModal',
  computed: {
    ...mapState(['documentTags']),
  },
};
</script>

Now, up inside my vuetify template for my modal I know how to create separate columns for the number of elements in my documentTags array. I can do something like this:

<template>
  <v-dialog v-model="show" max-width="600px">
    <v-row>
      <v-col
        v-for="n in this.documentTags.tags.length"
        :key="n"
        cols="18"
        sm="8"
        md="6"
      >
        <v-card class="pa-2" outlined tile>
          This v-card needs to be replaced with a v-select or v-text-field
        </v-card>
      </v-col>
    </v-row>
  </v-dialog>
</template>

However, I really don’t know how to walk through my documentTags array and determine which element (ie. v-select or v-text-field) needs to place in the appropriate column using the Vue and/or Vuetify syntax. Can anyone provide guidance on how this might be done?

Update

So, I updated my code according to Leonardo’s suggestion (which works), but now I’m running into an issue. I don’t know how to assign the tag Name to the label for each input object. I thought I could try something like ‘tag.Name’ but that doesn’t work. Any thoughts?

<template>
  <v-dialog v-model="show" max-width="600px">
    <v-row>
      <v-col
        v-for="tag in this.documentTags.tags"
        :key="tag.TagDefinitionId"
        cols="18"
        sm="8"
        md="6"
      >
        <v-select
          v-if="tag.InputType === 'Combobox'"
          :items="tag.TagValueOptions"
          item-text="Value"
          label="tag.Name"
          required
        >
        </v-select>
        <v-text-field
          v-else-if="tag.InputType === 'Textbox'"
           label="tag.Name"
           required
        >
        </v-text-field>
      </v-col>
    </v-row>
  </v-dialog>
</template>

Update2

Does anyone know how to access the value from the v-select or v-text-field since these elements are being dynamically created? Normally, I would use something like v-model=”name” and then declare the name property inside the data prop section… However, I’m not exactly sure how to do those in this instance. Ideally, I’d like to package the inputs up into a JSON object with the tag name and it’s value. So, something like this as an example (assuming we have two tags in this scenario).

"userDocumentTags": [{
    "Name": "Project Name",
    "Value": "My Project Name"
   },{
    "Name": "Project Number",
    "Value": "0001"
  }
]

I thought I could bind the property by doing something like this:

<v-select
  v-if="tag.InputType === 'Combobox'"
  :v-model="tag.Name"
  :items="tag.TagValueOptions"
  item-text="Value"
  label="tag.Name"
  required
></v-select>

And inside my data props section I have something like this:

data: () => ({
  userDocumentTags: [],   
}),

But, I don’t know how to push the name and value pairs into the userDocumentTags object. Can anyone provide any guidance here?

Advertisement

Answer

You can try something like this:

<template>
  <v-dialog v-model="show" max-width="600px">
    <v-row>
      <v-col
        v-for="tag in this.documentTags.tags"
        :key="tag.TagDefinitionId"
        cols="18"
        sm="8"
        md="6"
      >
        <v-select v-if="tag.InputType === 'Combobox'">
           // you can customize this v-select
        </v-select>
        <v-text-field v-else-if="tag.InputType === 'Textbox'">
           // you can customize this v-text-field
        </v-text-field>
      </v-col>
    </v-row>
  </v-dialog>
</template>

Update:

Hey dude, about you new doubt, in vue.js when you need pass a variable as a attribute you should use :attribute="var".

Check some examples:

label="tag.Name" // label is equal a string "tag.Name"
label="7" // label is equal a string "7"
:label="tag.Name" // label is equal a value of tag.Name
:label="7" // label is equal a number 7

In your case, switch label="tag.Name" by :label="tag.Name".

One thing else… Inside your template, you don’t need use the directive this to access your data values. In your for statement try use v-for="tag in documentTags.tags"

Update2

If you wanna apply the changes in “real-time” for all your application, you can use this way:
https://vuex.vuejs.org/guide/forms.html#form-handling

You’ll create a mutation method to update your vuex data and use the input or change event to call this mutation method.

Otherwise if you only wanna apply that changes after the user press a save button (per example), so you can’t use the documentTags as a computed data. You’ll need copy the documentTags value to your local data userDocumentTags. I guess you can do it in your mounted hook something like this:

mounted() {
   this.userDocumentTags = [...this.$store.getters['documentTags']];
}

Unfortunately, I couldn’t reproduce this example in my machine so I’m not sure if it’ll work. Maybe you should open a new question asking what’s the best way to resolve this new problem specifically.

User contributions licensed under: CC BY-SA
8 People found this is helpful
Advertisement