0

I have a MyList.vue which gets directly imported by my app.vue. MyList.vue doesnt contain subcomponents, it only imports:

import store from "../store/store";
import { USER_FETCHLIST } from "../store/actions/user";

And the data looks like this:

export default {
  data () {
    return {
      tableData: [],
      tableheader: []
    }
  },
  created: function(){
    store.dispatch(USER_FETCHLIST).then((res) => {

        this.tableData = res["data"]["tableData"]
        this.tableHeader = res["data"]["tableHeader"]
    })
},
  methods: {
    changeRecord: function(element){
      console.log(element)
    }
  }

}

MyList.vue has the following markup for a bootstrap-vue modal:

<template v-for="(element, index) in tableData">
   <tr>
    //rest of the markup generating the columns carrying the data
      <td>
        <button v-on:click="changeRecord(element)" v-b-modal="`modal-${index}`">Aendern</button>

        <b-modal :id="'modal-' + index" title="BootstrapVue">
      <template v-for="(value, name) in element">

        <template v-if="typeof value==='object'">
          <template v-for="(nestedValue, nestedName) in value">
            <span>{{nestedName}}</span>
            <input type="text" :value="nestedValue" :class="'editFieldDivision-' + index">
          </template>
        </template>

          <template v-else>
            <span>{{name}}</span>
            <input type="text" :value="value" :class="'editFieldDivision-' + index">
          </template>
     </template>
    </b-modal>
  </td>
</tr>
</template>

The endresult when clicking the button is this dialog:

https://i.sstatic.net/3yomu.jpg

The dialog might have more or less inputfields, depending on the data it receives from the backend.

However, this dialog is supposed to allow the user to apply changes to the respective record from the list in the background. Since I'm very new to vue, I don't know what the "vue-approach" to "grabbing" the user input would be. Should I use v-model? And if so, how do I do this, since the inserted data/observables are inserted dynamically. In the end, the data shall be put into a one-dimensional, where key-value has the "label" of the respective inputfield as key, and the value of the respective inputfield as value.

Furthermore, if the user discards the dialog, the changes inside the dialog shouldnt be applied to the datasets on the frontend.

1 Answer 1

1

Here's one way to accomplish what you're looking for.

Keep a reference to the original object, and create a copy. You will then use the copy in your inputs inside the modal, this way you wont be modifying the original object. Then on the hide event, check if the OK button was pressed, if it was you copy all the values from the copy to the original object.

If cancel is clicked (or the modal is closed in another way), you simply clear the selected object and the copy.

This solution uses the lodash.set method, so you will need to include this in your project.

I also moved your modal out of your table loop. Since you can only edit one record at a time, you only really need one modal on your page.

new Vue({
  el: "#app",
  data() {
    return {
      data: [{
          Internal_key: "TESTKEY_1",
          extensiontable_itc: {
            description_itc: "EXTENSION_ITC_1_1",
            description_itc2: "EXTENSION_ITC_1_2",
          },
          extensiontable_sysops: {
            description_sysops: "EXTENSION_SYSOPS_1"
          }
        },
        {
          Internal_key: "TESTKEY_2",
          extensiontable_itc: {
            description_itc: "EXTENSION_ITC_2_1",
            description_itc2: "EXTENSION_ITC_2_2",
          },
          extensiontable_sysops: {
            description_sysops: "EXTENSION_SYSOPS_2_1"
          }
        }
      ],
      editingRecord: {
        original: null,
        copy: null
      }
    }
  },
  methods: {
    onEditModalHide(event) {
      if (event.trigger === "ok") {
        // if OK is pressed, map values back to original object.
        for(let fullKey in this.editingRecord.copy){
          const copyObject = this.editingRecord.copy[fullKey]
          /*
            this uses lodash set funcktion 
            https://www.npmjs.com/package/lodash.set
          */
          set(this.editingRecord.original, fullKey, copyObject.value)
        }
      }
      
      this.editingRecord.original = null
      this.editingRecord.copy = null;
    },
    changeRecord(record) {
      const flatCopy = this.flattenObject(record);
      this.editingRecord.original = record;
      this.editingRecord.copy = flatCopy;

      this.$nextTick(() => {
        this.$bvModal.show('edit-modal')
      })
    },
    flattenObject(ob) {
      var toReturn = {};

      for (var i in ob) {
        if (!ob.hasOwnProperty(i)) continue;

        if ((typeof ob[i]) == 'object' && ob[i] !== null) {
          var flatObject = this.flattenObject(ob[i]);
          for (var x in flatObject) {
            if (!flatObject.hasOwnProperty(x)) continue;
            console.log(x)
            toReturn[i + '.' + x] = {
              key: x,
              value: flatObject[x].value
            };
          }
        } else {
          toReturn[i] = {
              key: i,
              value: ob[i] 
          };
        }
      }
      return toReturn;
    }
  }
});
<link href="//unpkg.com/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet" />
<link href="//unpkg.com/[email protected]/dist/bootstrap-vue.min.css" rel="stylesheet" />

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.11/vue.js"></script>
<script src="//unpkg.com/bootstrap-vue@latest/dist/bootstrap-vue.min.js"></script>
<script src="https://unpkg.com/[email protected]/index.js"></script>

<div id="app" class="p-4">
  <table class="table table-bordered">
    <tr v-for="element in data">
      <template v-for="field in element">
        <template v-if="typeof field==='object'">
          <td v-for="nestedObjectValue in field">
            {{nestedObjectValue}}
          </td>
        </template>
      <template v-else>
          <td>
            {{field}}
          </td>
        </template>
      </template>
      <td>
        <button class="btn btn-primary" @click="changeRecord(element)">
          Edit
        </button>
      </td>
    </tr>
  </table>
  <b-modal id="edit-modal" v-if="editingRecord.copy" @hide="onEditModalHide">
    <template v-for="obj in editingRecord.copy">
      <label>{{ obj.key }}</label>
      <input v-model="obj.value"  class="form-control"/>
    </template>
  </b-modal>
</div>

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

6 Comments

WoW, big thanks! I'm trying to apply this to my project now. Erm, can I also use vue-lodash? npmjs.com/package/vue-lodash is lodash.set just a part of vue-lodash? If not, how exactly do I integrate lodash.set after installation? The "docs" on your link just say: var set = require('lodash.set'); But where do I do this? And is this really enough? xD
I do not know if vue-lodash includes the set method (probably does), but if you only need lodash.set then there's no need to import a lot of other stuff. First run npm install lodash.set and then add import set from 'lodash.set' in the top of your <script> tag in your component.
Holy curb thanks, I got your code running in my project! :=) I will now try to integrate your approach into my site or rather replace my approach with yours ^^ holy moly this is great, thank you! :D I'll accept your answer and maybe come back to you here in the comments if I run into any further questions! :) You really saved my day! :D
Integrated neatly into my code, thx! :) I'm still trying to understand some parts of the code. The for loop in "flattenObject" it has "if (!ob.hasOwnProperty(i)) continue;" I tried to find out when the condition is met, but it never seems to be met. Putting a console log into it, the console.log never fired so the branch was never entered, neither for your nor for my data. Also, ob is a vue observable, it has tons of properties not relevant for the loop. Is this default vue or JS behavior that it "ignores" these properties and only loops over the relevant data?
@baryon123 I got that specific code from this answer - It's a check to see if the property is inherited or not. If it's inherited it will be skipped. You can read more about hasOwnProperty here
|

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.