4

I'm trying to implement a table that allows the users to edit a row by toggle with Edit button.

I was able to implement the toggle functionality but I ran into an issue where it toggles all rows instead of single row.

I believe that I have to select the row by index and with my implementation I'm able to do so but it seems I'm not able to find any use for it.

Vue.component('employee-data', {
    template:
        /*html*/
        `
        <b-container>
            <h3>Employee Data</h3>
            
            <b-pagination v-model="currentPage" :total-rows="rows" :per-page="perPage" 
                            aria-controls="employee-table"></b-pagination>
            <b-table striped hover :items="employees" id="employee-table"
                    :per-page="perPage" :current-page="currentPage" :fields="fields">

                <template v-slot:cell(employeeName)="row" v-if="edit">
                    <b-form-input v-model="row.item.employeeName"/>
                </template>

                <template v-slot:cell(joinDate)="row" v-if="edit">
                    <b-form-input v-model="row.item.joinDate"/>
                </template>

                <template v-slot:cell(selectedDepartment)="row" v-if="edit">
                    <b-form-input v-model="row.item.selectedDepartment"/>
                </template>
                
                <template v-slot:cell(jobDescription)="row" v-if="edit">
                    <b-form-input v-model="row.item.jobDescription"/>
                </template>

                <template v-slot:cell(actions)="row">
                    <b-button @click="toggleEdit(row.index)">
                        {{ edit ? 'Save' : 'Edit' }}
                    </b-button>
                </template>

            </b-table>
        </b-container>
    `,
    props: {
        employees: {
            type: Array,
            required: true
        }
    },
    data() {
        return {
            edit: false,
            perPage: 3,
            currentPage: 1,
            fields: [
                {
                    key: 'employeeName',
                    label: 'Employee Name',
                    sortable: true
                  },
                  {
                    key: 'joinDate',
                    label: 'Join Date',
                    sortable: true
                  },
                  {
                    key: 'selectedDepartment',
                    label: 'Selected Department',
                    sortable: true,
                  },
                  {
                    key: 'jobDescription',
                    label: 'Job Description',
                    sortable: true,
                  },
                  {
                    key: 'actions',
                    label: 'Actions',
                    sortable: false,
                  }
              ]
        }
    },
    computed: {
        rows() {
            return this.employees.length
        }
    },
    methods: {
        toggleEdit(index){
            this.edit = !this.edit
        }
    }
})

Edit: Here is the JSFiddle showing the issue

1 Answer 1

8

If you have a unique identifier (like an id) and only want to allow one row to be edited at a time, you can set your edit variable to the id of the row currently being edited.

You can also use the generic slot v-slot:cell(), to reduce the amount of templates you write.

Example

new Vue({
  el: "#app",
  data() {
    return {
      edit: null,
      employees: [{
          id: 0,
          employeeName: "Jane",
          joinDate: "11-11-1111",
          selectedDepartment: "IT",
          jobDescription: "Nerd"
        },
        {
          id: 1,
          employeeName: "Peter",
          joinDate: "12-12-1212",
          selectedDepartment: "Accounting",
          jobDescription: "Moneier"
        }
      ],
      fields: [{
          key: 'employeeName',
          label: 'Employee Name',
          sortable: true
        },
        {
          key: 'joinDate',
          label: 'Join Date',
          sortable: true
        },
        {
          key: 'selectedDepartment',
          label: 'Selected Department',
          sortable: true
        },
        {
          key: 'jobDescription',
          label: 'Job Description',
          sortable: true
        },
        {
          key: 'actions',
          label: 'Actions'
        }
      ]
    }
  },
  computed: {
    rows() {
      return this.employees.length
    }
  },
  methods: {
    onEdit(id) {
      this.edit = this.edit !== id ? id : null;
    }
  }
})
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/bootstrap-vue/2.18.1/bootstrap-vue.min.css" />

<script src="//cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/bootstrap-vue/2.18.1/bootstrap-vue.min.js"></script>

<div id="app">
  <b-table striped hover :items="employees" :fields="fields">
    <template v-slot:cell()="{ value, item, field: { key }}">
      <template v-if="edit != item.id">{{ value }}</template>
    <b-form-input v-else v-model="item[key]" />
    </template>

    <template v-slot:cell(actions)="{ item: { id }}">
        <b-dropdown variant="primary" text="Actions">
          <b-dropdown-item @click="onEdit(id)">{{ edit === id ? 'Save' : 'Edit' }}</b-dropdown-item>
          <b-dropdown-divider></b-dropdown-divider>
          <b-dropdown-item @click="onDelete(id)">Delete</b-dropdown-item>
        </b-dropdown>
      </template>
  </b-table>
</div>

If you don't have a unique identifier for each row, or want to be able to edit multiple rows at a time, you can add a flag to your item (isEditing in the example), that will toggle whether the row is currently being edited or not.

Example 2

new Vue({
  el: "#app",
  data() {
    return {
      employees: [{
          id: 0,
          employeeName: "Jane",
          joinDate: "11-11-1111",
          selectedDepartment: "IT",
          jobDescription: "Nerd"
        },
        {
          id: 1,
          employeeName: "Peter",
          joinDate: "12-12-1212",
          selectedDepartment: "Accounting",
          jobDescription: "Moneier"
        }
      ],
      fields: [{
          key: 'employeeName',
          label: 'Employee Name',
          sortable: true
        },
        {
          key: 'joinDate',
          label: 'Join Date',
          sortable: true
        },
        {
          key: 'selectedDepartment',
          label: 'Selected Department',
          sortable: true
        },
        {
          key: 'jobDescription',
          label: 'Job Description',
          sortable: true
        },
        {
          key: 'actions',
          label: 'Actions'
        }
      ]
    }
  },
  computed: {
    rows() {
      return this.employees.length
    }
  },
  methods: {
    onEdit(item) {
      if (item.isEditing)
        item.isEditing = false;
      else
        this.$set(item, 'isEditing', true)
    }
  }
})
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/bootstrap-vue/2.18.1/bootstrap-vue.min.css" />

<script src="//cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/bootstrap-vue/2.18.1/bootstrap-vue.min.js"></script>

<div id="app">
  <b-table striped hover :items="employees" :fields="fields">
    <template v-slot:cell()="{ value, item, field: { key }}">
      <template v-if="!item.isEditing">{{ value }}</template>
    <b-form-input v-else v-model="item[key]" />
    </template>

    <template v-slot:cell(actions)="{ item }">
        <b-dropdown variant="primary" text="Actions">
          <b-dropdown-item @click="onEdit(item)">{{ item.isEditing ? 'Save' : 'Edit' }}</b-dropdown-item>
          <b-dropdown-divider></b-dropdown-divider>
          <b-dropdown-item>Delete</b-dropdown-item>
        </b-dropdown>
      </template>
  </b-table>
</div>

Sign up to request clarification or add additional context in comments.

1 Comment

Excellent! Thank you!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.