Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamically create Vue Component on click

I have a list of orders which I am populating via the orders array. Each of these orders have a functionality where the user can edit these orders. I am trying to generate a vuetify dialog box component when the user clicks on the edit button which will have the default data from that order which the user can choose to edit. So far I have tried this,

<tbody class="items">
  <tr v-for="order in orders" :key="order.name">
    <td>{{ order.fullname }}</td>
    <td>{{ order.address }}</td>
    <td>{{ order.phone }}</td>
    <!-- <td>{{ order.orderQuantity }}</td> -->
    <td>{{ order.quantity }}</td>
    <td v-if="order.status == true">
      <v-chip small outlined class="ma-2 chip" color="green">delivered</v-chip>
    </td>
    <td v-if="order.status == false">
      <v-chip small outlined class="ma-2 chip" color="red">in progress</v-chip>
    </td>
    <td>
      <!-- <component v-bind:is="component"></component> -->
      <!-- <EditOrder></EditOrder> -->
      <v-dialog v-model="dialog" persistent max-width="290">
        <template #activator="{ on, attrs }">
          <v-btn icon color="primary" dark v-bind="attrs" v-on="on"> Open Dialog </v-btn>
        </template>
        <v-card>
          <v-card-title class="headline"> Use Google's location service? </v-card-title>
          <v-card-text>{{ phone }}</v-card-text>
          <v-card-actions>
            <v-spacer></v-spacer>
            <v-btn color="green darken-1" text @click="dialog = false"> Disagree </v-btn>
            <v-btn color="green darken-1" text @click="dialog = false"> Agree </v-btn>
          </v-card-actions>
        </v-card>
      </v-dialog>
    </td>
  </tr>
</tbody>

but this generates n instances of the dialog box before the actual components loads. I also tried using async components but couldn't figure out where I was wrong.

This is what I think should happen; When the user clicks on the "edit order" button, a dialog box component with the default order details for that particular order must be automatically generated and destroyed. Please correct me if I am wrong.

like image 808
Anuj Kaithwas Avatar asked Sep 07 '25 15:09

Anuj Kaithwas


2 Answers

  1. Move the dialog part to a separate component
  2. In the parent component, use it once at the beginning with dialog=false to be hidden initially
  3. Add a data attribute orderToEdit which will be a prop to the child component
  4. For each order in the list, add an edit button that would call a method when clicked passing the order
  5. In this method, set orderToEdit to the passed order of the clicked item and set dialog=true to show it with the custom values

const dialogmodel = Vue.component('btn', {
  template: '#dialogmodel',
  props: { order: Object, value: Boolean },
  computed: {
    dialog: {
      get () { return this.value; },
      set (value) { this.$emit('close', value); }
    }
  }
});

new Vue({
  el:"#app",
  vuetify: new Vuetify(),
  components: { dialogmodel },
  data: () => ({
    orders: [
      { name:"1", fullname:"fullname1", address:"address1", phone:"phone1", quantity:"quantity1", status:false },
      { name:"2", fullname:"fullname2", address:"address2", phone:"phone2", quantity:"quantity2", status:true }
    ],
    dialog:false,
    orderToEdit: {}
  }),
  methods: {
    closeDialog(value) { this.dialog = value; },
    editOrder(order) { this.orderToEdit = order; this.dialog = true; }
  }
});
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vuetify.js"></script><link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/@mdi/[email protected]/css/materialdesignicons.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/vuetify.min.css" rel="stylesheet">

<template id="dialogmodel">
  <div>
    <v-dialog v-model="dialog" max-width="290" persistent>
      <v-card>
        <v-card-title class="headline">
          {{ order.fullname }}
        </v-card-title>
        <v-card-text> {{ order.address }} </v-card-text>
        <v-card-actions>
          <v-spacer></v-spacer>
          <v-btn color="green darken-1" text @click="$emit('close')">
            Disagree
          </v-btn>
        </v-card-actions>
      </v-card>
    </v-dialog>
  </div>
</template>

<v-app id="app">
  <div>
    <dialogmodel v-model="dialog" :order="orderToEdit" @close="closeDialog" />
  </div>
  <div>
    <table>
      <tbody class="items">
        <tr v-for="order in orders" :key="order.name">
          <td>{{ order.fullname }}</td>
          <td>{{ order.address }}</td>
          <td>{{ order.phone }}</td>
          <td>{{ order.quantity }}</td>
          <td v-if="order.status == true">
            <v-chip small outlined class="ma-2 chip" color="green">delivered</v-chip>
          </td>
          <td v-if="order.status == false">
            <v-chip small outlined class="ma-2 chip" color="red">in progress</v-chip>
          </td>
          <td>
            <v-btn @click="editOrder(order)">Edit</v-btn>
          </td>
        </tr>
      </tbody>
    </table>
  </div>
</v-app>
like image 93
Majed Badawi Avatar answered Sep 09 '25 12:09

Majed Badawi


Are you looking to have a central popup being filled with the data of the order you selected to edit? In that case you can simply have a single component for the edit popup (instead of adding them to your v-for).

You can do it like this:

  1. When clicking on 'edit', set a data property 'orderIndexToEdit' to the index of the order in the v-for
  2. have a single dialog box that becomes visible when 'orderIndexToEdit' is truethy using v-if or v-show
  3. Create a computed property that returns the correct order from the orders array, based on the index stored in the 'orderIndexToEdit' variable.
  4. Use that computed property to populate the dialog box.

Let me know whether this helps!

like image 38
CVE Avatar answered Sep 09 '25 10:09

CVE