Home > Software engineering >  remove checkbox items from anywhere in an array
remove checkbox items from anywhere in an array

Time:10-13

Suppose I have a parent checkbox and under this I have four child checkboxes. For example-

      -Animals
          -Cat
          -Dog
          -Squirrel
          -Lion

If I select the parent, then all the childs will be selected. If I deselect all the child, the parent will be unselected. If I select only one child then also the parent will be selected. How to implement this in vue.js ?

json:

{
  "id": 266,
  "parent_id": 0,
  "menu_name": "Server && DB Info",
  "menu_url": "#",
  "menu_icon": "fa fa-database",
  "order_by": 6,
  "has_child": 1,
  "child": [
    {
      "id": 269,
      "parent_id": 266,
      "menu_name": "Client",
      "menu_url": "/server_and_db_info/client",
      "menu_icon": null,
      "order_by": 7,
      "has_child": 0,
      "child": []
    },
    {
      "id": 268,
      "parent_id": 266,
      "menu_name": "VPN",
      "menu_url": "/server_and_db_info/vpn",
      "menu_icon": null,
      "order_by": 8,
      "has_child": 0,
      "child": []
    },
    {
      "id": 262,
      "parent_id": 266,
      "menu_name": "Server",
      "menu_url": "/server_and_db_info/server",
      "menu_icon": "",
      "order_by": 9,
      "has_child": 0,
      "child": []
    },
    {
      "id": 267,
      "parent_id": 266,
      "menu_name": "Database",
      "menu_url": "/server_and_db_info/database",
      "menu_icon": null,
      "order_by": 10,
      "has_child": 0,
      "child": []
    },
    {
      "id": 271,
      "parent_id": 266,
      "menu_name": "Domain",
      "menu_url": "/server_and_db_info/domain",
      "menu_icon": null,
      "order_by": 15,
      "has_child": 0,
      "child": []
    }
  ]
}

Code:

<div
                            class="menu-padding padding-top"
                            v-for="(data, index) in getAllMenu"
                            :key="index"
                          >
                            <input
                              type="checkbox"
                              v-model="selectedMenu"
                              :value="data.id"
                              @change="select(data.id, index)"
                            />
                            <label>{{ data.menu_name }}</label>
                            <div
                              class="padding-left"
                              v-if="data.child.length > 0"
                            >
                              <div
                                v-for="(data_child, index) in data.child"
                                :key="index"
                              >
                                <input
                                  type="checkbox"
                                  v-model="selectedMenu"
                                  :value="data_child.id"
                                  @change="selectChild(data.id, data_child.id)"
                                />
                                <label
                                  >{{ data_child.menu_name }}
                                  {{ data_child.id }}</label
                                >
                              </div>
                            </div>
                          </div>

CodePudding user response:

Try like following snippet:

new Vue({
  el: '#demo',
  data() {
    return {
      getAllMenu: [
        {"id": 256, "menu_name": "Server", "child": []},
        {"id": 266, "menu_name": "Server && DB Info", "child": [
          {"id": 269, "menu_name": "Client",},
          {"id": 268, "menu_name": "VPN",},
          {"id": 262, "menu_name": "Server",},
          {"id": 267, "menu_name": "Database",},
          {"id": 271, "menu_name": "Domain",}
        ]},
        {"id": 286, "menu_name": "Info", "child": [
          {"id": 289, "menu_name": "C",},
          {"id": 288, "menu_name": "V",},
          {"id": 282, "menu_name": "S",},
          {"id": 287, "menu_name": "D",},
        ]},
      ],
      selectedMenu: []
    }
  },
  methods: {
    selChild(id) {
      return this.selectedMenu.find(s => s.child.find(c => c.id === id))
    },
    selParent(id) {
      return this.selectedMenu.find(s => s.id === id)
    },
    select(data) {
      if(this.selectedMenu.find(s => s.id === data.id)) {
        this.selectedMenu = this.selectedMenu.filter(s => s.id !== data.id)
      } else {
        this.selectedMenu.push(data)
      }
    },
    selectChild(id, childId) {
      if (this.selectedMenu.length) {
        let par = this.selectedMenu.find(s => s.id === id)
        let parent = {...par}
        let idx = this.getAllMenu.findIndex(f => f.id === id)
        if (par && parent.child.find(p => p.id === childId)) {
          if(parent.child.length > 1) {
            parent.child = parent.child.filter(c => c.id !== childId)
            let i = this.selectedMenu.findIndex(f => f.id === parent.id)
            this.selectedMenu = this.selectedMenu.filter(m => m.id !== parent.id)
            this.selectedMenu.push(parent)
          } else {
            this.selectedMenu = this.selectedMenu.filter(s => s.id !== id)
          }
        } else {
          let ch = [...this.getAllMenu]
          if(par) {
            ch = ch[idx].child.filter(f => f.id === childId)
            parent.child.push(...ch)
          } else {
            par = this.getAllMenu.find(f => f.id === id)
            let parent = {...par}
            parent.child = parent.child.filter(f => f.id === childId)
            this.selectedMenu.push(parent)
          }
        }
      } else {
        let par = this.getAllMenu.find(s => s.id === id)
        let parent = {...par}
        parent.child = parent.child.filter(c => c.id === childId)
        this.selectedMenu.push(parent)
      }
    }
  }
})

Vue.config.productionTip = false
Vue.config.devtools = false
.padding-left {
  padding-left: 20px;
}
ul {
  list-style: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="demo">
  <ul>
    <li
      class="menu-padding padding-top"
      v-for="(data, index) in getAllMenu"
      :key="data.id"
    >
      <input type="checkbox" :value="data.id" @change="select(data)" :checked="selParent(data.id)" />
      <label>{{ data.menu_name }}</label>
      <ul class="padding-left" >
        <li v-for="data_child in data.child" :key="data_child.id">
          <input type="checkbox" :value="data_child.id" @change="selectChild(data.id, data_child.id)" :checked="selChild(data_child.id)" />
          <label>{{ data_child.menu_name }} - {{ data_child.id }}</label>
        </li>
      </ul>
    </li>
  </ul>
  <p>{{ selectedMenu }}</p>
</div>

CodePudding user response:

The problem is, that you provided the same v-model to all of your checkboxes without reference for each of it. That means, that v-model="selectedMenu" get´s overwritten by every new value that is passed by any element that uses this v-model.

As your data is nested, you should pre-build the v-model for this. Define a object like this:

myVModel: {}

This function will check, if your provided data, in this case named myObject, has childs, if yes, it will expand your v-model myModel for each child:

preBuildVModel() {
      Object.keys(this.myObject).forEach(parentId => {
        if(this.myObject[parentId].has_child === 1) {
          Object.assign(this.myVModel, {[this.myObject[parentId].id]: {
              'parent': null,
              'childs': {}
            }
          })
          Object.keys(this.myObject[parentId].child).forEach(childId => {
            console.log(this.myObject[parentId].child[childId].id)
            Object.assign(this.myVModel[this.myObject[parentId].id].childs,
                {[this.myObject[parentId].child[childId].id]: null
            })
          });
        }
        else {
          Object.assign(this.myVModel, {[this.myObject[parentId].id]: {
              'parent': null
            }
          })
        }
      });
    }

I added another parent to your provided data, as I asume there are multiple parents and also the case, that there are no parents. In my example there is another parent with id 100 and no children. The myVModel would look like this:

{
   "100":{
      "parent":null
   },
   "266":{
      "parent":null,
      "childs":{
         "262":null,
         "267":null,
         "268":null,
         "269":null,
         "271":null
      }
   }
}

So you need to fix your hook now, as your v-model needs to be set correctly. I will set them to my examples, just change them to the declared names you used:

<div class="menu-padding padding-top" v-for="(data, index) in myObject" :key="index">
        <input
            type="checkbox"
            v-model="myVModel[data.id].parent"
            :value="data.id"
        />
        <label>{{ data.menu_name }}</label>
        <div class="padding-left" v-if="data.child.length > 0">
          <div v-for="(data_child, index) in data.child" :key="index">
            <input
                type="checkbox"
                v-model="myVModel[data.id].childs[data_child.id]"
                :value="data_child.id"
            />
            <label>{{ data_child.menu_name }} {{ data_child.id }}</label>
          </div>
        </div>
      </div>

I deleted your @change as you not provided them.

CodePudding user response:

I have done it like this from another source and it's working:

  <div
    class="menu-padding padding-top"
    v-for="(data, index) in getAllMenu"
    :key="index"
  >
    <input
      type="checkbox"
      v-model="selectedMenu"
      :value="data.id"
      @change="select(data.id, index)"
    />
    <label>{{ data.menu_name }}</label>
    <div
      class="padding-left"
      v-if="data.child.length > 0"
    >
      <div
        v-for="(data_child, index_child) in data.child"
        :key="index_child"
      >
        <input
          type="checkbox"
          v-model="selectedMenu"
          :value="data_child.id"
          @change="
            selectChild(data.id, data_child.id, index)
          "
        />
        <label>{{ data_child.menu_name }}</label>
      </div>
    </div>
  </div>

And the functions:

selectChild(id, child_id, index) {
      this.$nextTick(function () {
        const self = this;
        if (this.selectedMenu.includes(child_id)) {
          this.selectedMenu.push(id);

          this.selectedMenu = [...new Set(this.selectedMenu)];
          //  console.log(this.selectedMenu);
        } else {
          var arrData = false;
          this.getAllMenu[index].child.forEach((data) => {
            console.log(data.id);

            if (self.selectedMenu.includes(data.id)) {
              arrData = true;
            }
          });

          if (arrData === false) {
            console.log(this.getAllMenu[index].id);
            this.selectedMenu = this.arrayRemove(
              this.selectedMenu,
              this.getAllMenu[index].id
            );
          }

          // this.selectedMenu.pop(id)
          this.selectedMenu = [...new Set(this.selectedMenu)];
        }
      });
    },
    select(id, index) {
      this.$nextTick(function () {
        if (this.selectedMenu.includes(id)) {
          if (this.getAllMenu[index].child.length > 0) {
            for (let i = 0; i < this.getAllMenu[index].child.length; i  ) {
              this.selectedMenu.push(this.getAllMenu[index].child[i].id);
            }
            console.log(this.selectedMenu);
          }
        } else {
          for (let i = 0; i < this.getAllMenu[index].child.length; i  ) {
            this.selectedMenu = this.arrayRemove(
              this.selectedMenu,
              this.getAllMenu[index].child[i].id
            );
          }
          console.log(this.selectedMenu);
        }
      });

      //console.log(this.selectedMenu)
    },

    arrayRemove: function (arr, value) {
      return arr.filter(function (ele) {
        return ele != value;
      });
    },
  • Related