Skip to content
Advertisement

creating abstract components that can manage external data

Currently I use Vuetify for base components and would like to create reusable extensions. For example a list containing checkboxes, a datatable column with some functionality etc.

For this question I will take the list containing checkboxes example. I created the following component called CheckboxGroup.vue

<template>
  <v-container>
    <v-checkbox
      v-for="(item, index) in items"
      :key="index"
      v-model="item.state"
      :label="item.title"
    ></v-checkbox>
  </v-container>
</template>

<script>
export default {
  props: {
    items: Array,
    required: true
  }
};
</script>

This component takes an array of objects as a property and creates a checkbox for each entry.

Important parts are v-model="item.state" and :label="item.title". Most of the time the state attribute will have a different name, same for the title attribute.

For testing purposes I created a view file called Home.vue holding an array of documents.

<template>
  <v-container>
    <CheckboxGroup :items="documents"/>
    <v-btn @click="saveSettings">Save</v-btn>
  </v-container>
</template>

<script>
import CheckboxGroup from "../components/CheckboxGroup";

export default {
  components: {
    CheckboxGroup
  },
  data: function() {
    return {
      documents: [
        {
          id: 1,
          name: "Doc 1",
          deleted: false
        },
        {
          id: 2,
          name: "Doc 2",
          deleted: false
        },
        {
          id: 3,
          name: "Doc 3",
          deleted: true
        }
      ]
    };
  },
  methods: {
    saveSettings: function() {
      console.log(this.documents);
    }
  }
};
</script>

This time title is called name and state is called deleted. Obviously CheckboxGroup is not able to manage the documents because the attribute names are wrong.

How would you solve this problem? Would you create a computed property and rename these attributes? Would be a bad idea I think…

And by the way, is using v-model a good idea? A different solution would be to listen to the changed event of a checkbox and emit an event with the item index. Then you would have to listen for the change in the parent component.

I don’t think there is a way to create something like

<CheckboxGroup :items="documents" titleAttribute="name" stateAttribute="deleted"/> 

because it would be bad design anyway. I hope that this is a very trivial problem and every Vue developer has been confronted with it, since the primary goal should always be to develop abstract components that can be reused multiple times.

Please keep in mind that this checkbox problem is just an example. A solution for this problem would also solve same or similar problems 🙂

Advertisement

Answer

If I understood what you wanted, it`s not so trivial. Using props is a good idea. You dont need to manage the documents attribute names, just set the attribute name to your component.

Note

Renaming the attributes or using proxies is more resource-intensive like this solution, because you need to run loop to rename the attribute names or apply aliases to data array objects.

Example

CheckboxGroup.vue

  <template>
      <v-container fluid>
        <v-checkbox 
          v-for="(item, index) in items"
          :key="index"
          v-model="item[itemModel]" 
          :label="item[itemValue]"
        ></v-checkbox>
        <hr>
        {{items}}
      </v-container>
    </template>
    <script>

    export default {
      name: "CheckboxGroup",
       props: {

        items: {
          type: Array,
          required:true
        },

        itemValue:{
          type:String,
          default: 'title',

           // validate props if you need
          //validator: function (value) {
          //  return ['title', 'name'].indexOf(value) !== -1
          // }
          // or make required
        },

        itemModel:{
          type:String,
          default: 'state',

           // validate props if you need
           //validator: function (value) {
            // validate props if you need
            // return ['state', 'deleted'].indexOf(value) !== -1
           // }
         // or make required
        }

      }
    };
    </script>

Home.vue

<template>

  <div id="app">
    <checkbox-group :items="documents"
      item-value="name"
      item-model="deleted"
    >

    </checkbox-group>
  </div>
</template>

<script>
import CheckboxGroup from "./CheckboxGroup.vue";

export default {
  name: "App",
  components: {
    // HelloWorld,
    CheckboxGroup
  },
  data: function() {
    return {
      documents: [
        {
          id: 1,
          name: "Doc 1",
          deleted: false
        },
        {
          id: 2,
          name: "Doc 2",
          deleted: false
        },
        {
          id: 3,
          name: "Doc 3",
          deleted: true
        }
      ]
    }
}
};
</script>

Based on your example I`v tried to show how to create component to managing object attributes in child component. If you need more information, please let me know.

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