vue.jsvuetify.jsv-data-tablevuetify-datatable

How to insert text search field for each table header for multi search function dynamically with v-for?


In order to achieve for multi search function we add a search field at each header with v-slot where we define the header name directly. For example:

      <template v-slot:header.NAME="{ header }">
        {{ header.text }}
        <v-menu offset-y :close-on-content-click="false">
          <template v-slot:activator="{ on, attrs }">

            <v-text-field v-bind="attrs" v-on="on"
              v-model="nameSearch"
              class="pa"
              type="text"
            ></v-text-field>
          </template>         
        </v-menu>
      </template>  

Now I have around 50 headers for my data table. My idea is to render dynamically. I tried with v-for but neither it renders and or give any error. Code below:

        <template v-for="(x, index) in this.headers" slot="headers" slot-scope="props">
        {{ props.x.text }}
        <v-menu  offset-y :close-on-content-click="false" :key="index">
          <template v-slot:activator="{ on, attrs }">


            <v-text-field v-bind="attrs" v-on="on"
              v-model="searchObj"
              class="pa"
              type="text"
            ></v-text-field>

          </template>
          
        </v-menu>
      </template>  

what did i missed here ? or it is completely wrong way i went ?

Below is the full code:

    <template>
      <v-app class="main-frame">
      <v-main>
            <v-data-table
              :headers="headers"
              :items="filteredData"
            >
              <template v-slot:header.NAME="{ header }">
                {{ header.text }}
                <v-menu offset-y :close-on-content-click="false">
                  <template v-slot:activator="{ on, attrs }">

                    <v-text-field v-bind="attrs" v-on="on"
                      v-model="nameSearch"
                      class="pa"
                      type="text"
                    ></v-text-field>

                  </template>
                  
                </v-menu>
              </template>        

              <template v-slot:header.DEPARTMENT="{ header }">
                {{ header.text }}
                <v-menu offset-y :close-on-content-click="false">
                  <template v-slot:activator="{ on, attrs }">


                    <v-text-field v-bind="attrs" v-on="on"
                      v-model="departmentSearch"
                      class="pa"
                      type="text"
                    ></v-text-field>

                  </template>
                  
                </v-menu>
              </template>  

          </v-data-table>
      </v-main>
      </v-app>
    </template>

    <script>

    import axios from "axios";

    }
    export default {
      name: 'Home',

      data() {
        return {
        /* Table data */ 
          headers: []
          allData: [],

          /* Column Search */
          nameSearch: '',
          departmentSearch: ''

        }
      },

      computed: {

        filteredData() {
          
          let conditions = [];
          
          if (this.nameSearch) {
            conditions.push(this.filterName);
          }

          if (this.departmentSearch) {
            conditions.push(this.filterDepartment);
          }
          
          if (conditions.length > 0) {
            return this.allData.filter((data) => {
              return conditions.every((condition) => {
                return condition(data);
              })
            })
          }
          
          return this.allData;
        }
      },


      mounted () {
        this.getAllData() 
      },

      methods: {
        getAllData() {
        this.allData = []

        axios
          .get("http://localhost:5001/"}
          .then(res => {
            if (res.status === 200) {
              if (res.data === 0) {
                console.log("Something is wrong !!!")
              } else {
                this.allData = res.data["allData"]

                this.headers = res.data["allDataHeaders"]

              }
            }
          
          }).catch(error => {
            console.log(error);
            
          })
      },

      filterName(item) {
          return item.NAME.toLowerCase().includes(this.nameSearch.toLowerCase())
      },

      filterDepartment(item) {
          return item.DEPARTMENT.toLowerCase().includes(this.departmentSearch.toLowerCase())
      },
        
    }
      
    }
    </script>

Solution

  • I have found the solution myself:

            <template v-for="(header, i)  in headers" v-slot: 
            [`header.${header.value}`]="{  }"> 
            <div @click.stop :key="i">
             {{ header.text }}
                <v-text-field :key="i"
                  v-model="multiSearch[header.value]"
                  class="pa"
                  type="text"
                  :placeholder="header.value"
                  prepend-inner-icon="mdi-magnify"
                ></v-text-field>
             </div>
            </template>
    

    In data, I have an empty object called:

    searchObj: {}
    

    and in computed method, i have the following method:

    filteredData() {
    
      if (this.searchObj) {
        return this.allData.filter(item => {
          return Object.entries(this.searchObj).every(([key, value]) => {
    
            return (item[key] || '').toLowerCase().includes(value.toLowerCase())
            
          })
        })
      } else {
        return this.allData
      } 
    },
    

    Full solution below:

      <template>
      <v-app class="main-frame">
      <v-main>
            <v-data-table
              :headers="headers"
              :items="filteredData"
            >
    
            <template v-for="(header, i)  in headers" v-slot: 
             [`header.${header.value}`]="{  }"> 
             {{ header.text }}
              <div @click.stop :key="i">
                <v-text-field :key="i"
                  v-model="multiSearch[header.value]"
                  class="pa"
                  type="text"
                  :placeholder="header.value"
                  prepend-inner-icon="mdi-magnify"
                ></v-text-field>
              </div>
            </template>
    
          </v-data-table>
      </v-main>
      </v-app>
    </template>
    
    <script>
    
    import axios from "axios";
    
    }
    export default {
      name: 'Home',
    
      data() {
        return {
        /* Table data */ 
          headers: []
          allData: [],
          searchObj: {},
        }
      },
    
      computed: {
    
        filteredData() {
    
          if (this.searchObj) {
            return this.allData.filter(item => {
              return Object.entries(this.searchObj).every(([key, value]) => {
    
                 return (item[key] ||'').toLowerCase().includes(value.toLowerCase())
    
                 })
            })
          } else {
              return this.allData
            } 
         },
      },
    
    
      mounted () {
        this.getAllData() 
      },
    
      methods: {
        getAllData() {
        this.allData = []
    
        axios
          .get("http://localhost:5001/"}
          .then(res => {
            if (res.status === 200) {
              if (res.data === 0) {
                console.log("Something is wrong !!!")
              } else {
                this.allData = res.data["allData"]
    
                this.headers = res.data["allDataHeaders"]
    
              }
            }
          
          }).catch(error => {
            console.log(error);
            
          })
      },
    
    }
      
    }
    </script>