【问题标题】:Multiple select inputs in table header with unique models具有唯一模型的表头中的多个选择输入
【发布时间】:2020-06-11 14:33:01
【问题描述】:

我正在从后端接收采用以下格式的数据

[ 
    [ 
        [ "123", "21/11/2013", "Data", "Data" ], 
        [ "234", "22/11/2013", "Data", "Data" ], 
        [ "345", "12/09/2018", "Data", "Data" ], 
    ], 
    [ 
        [ "123", "Data", "Data", "Data", "Data", "Data", "Data", "Data", "Data", "Data", "Data" ], 
        [ "234", "Data", "Data", "Data", "Data", "Data", "Data", "Data", "Data", "Data", "Data" ], 
        [ "345", "Data", "Data", "Data", "Data", "Data", "Data", "Data", "Data", "Data", "Data" ] 
    ] 
]

每个 fileData 代表一个表,因此在上面的示例中它应该生成两个表。里面的数据包含一个表行,所以上面的每个表都有两行。 为了实现这一点,我正在做类似以下的事情。

<table class="table" v-for="(file, index) in fileData" :key="index">
    <tbody>
        <tr v-for="(row, index2) in file":key="index2">
          <td v-for="(data, index3) in row" :key="index3">
            {{ data }}
          </td>
        </tr>
    </tbody>
</table>

这一切似乎都很好。但是,我使用的数据没有标题,但我需要为包含选择的每一列提供一个标题。因此,我添加了以下内容

<table class="table" v-for="(file, index) in fileData" :key="index">
    <thead>
        <tr>
           <th scope="col" v-for="(col, index2) in file[index]" :key="index2">
             <b-form-select v-model="options.value" :options="options"></b-form-select>
           </th>
        </tr>
    </thead>
</table>

这似乎又一次奏效了。我的问题是我希望用户使用选择来定义列代表的内容。目前,如果我选择某些东西,它们都会改变。

我以这个 Fiddle 为例 https://jsfiddle.net/mhyv62bt/1/

如何使选择独立,是否也可以在选择后删除选项?

谢谢

这似乎为每个表生成了正确数量的标题列。

更新 我的设置略有不同,因此尝试将其与我的项目相适应。因此,我创建了文件 THeadSelect.vue

<template id="theadselect">
  <thead>
  <tr>
    <th
      v-for="(i,index) in this.length_"
      :key="index"
    >
      <select
        v-model="headers[index]">
        <option disabled value="">
          Please select one
        </option>

        <option
          v-if="headers[index]"
          selected
        >
          {{headers[index]}}
        </option>
        <option
          v-for="option in filteredOptions"
          :key="option"
        >
          {{option}}
        </option>
      </select>
    </th>
  </tr>
  </thead>
</template>

<script>
export default {
  mounted () {
    this.$emit('update:headers',
      this.headers
        .concat(Array.from({ length: this.length_ }, _ => ''))
        .slice()
    )
  },
  props: {
    options: {
      type: Array,
      required: true
    },
    length: Number,
    headers: {
      type: Array,
      required: true
    }
  },
  computed: {
    length_: {
      get () {
        return this.length || this.options.length
      },
      set (l) {
        this.$emit('update:length', l)
      }
    },
    filteredOptions () {
      return this.options.filter(
        option => !this.headers.includes(option)
      )
    }
  }
}
</script>

然后我尝试在我的页面中使用它

<template>
  <div>
    <b-form
      novalidate
      @submit.stop.prevent=""
    >
      <div class="row">
        <div class="col-12">
          <table class="table table-bordered" v-for="(file, index) in fileData" :key="index">
            <thead
              is="THeadSelect"
              :options="['option1', 'option2', 'option3']"
              :headers.sync="headers"
            ></thead>
            <tbody>
            <tr v-for="(row, index2) in file" :key="index2">
              <td v-for="(data, index3) in row" :key="index3">
                {{ data }}
              </td>
            </tr>
            </tbody>
          </table>
        </div>
      </div>
    </b-form>
  </div>
</template>

<script>
import { THeadSelect } from '@/components/common/THeadSelect'

export default {
  components: {
    THeadSelect
  },
  computed: {
    fileData () {
      return this.$store.getters.fileData
    }
  },
  data () {
    return {
      headers: [],
      length: 10,
    }
  }
}
</script>

虽然有点乱。每个表只显示 3 个选择。此外,如果我在表 1 中选择一个选项,它会在表 2 中选择相同的选项。如果您查看我的原始小提琴,您可以看到我正在尝试使用的初始数据,因此总会有两个表。

【问题讨论】:

    标签: vue.js


    【解决方案1】:
    <div id='app'>
    <table class="table table-bordered" v-for="(file, index) in fileData" :key="index">
        <thead>
      <tr>
        <th scope="col" v-for="(col, index2) in file[index]" :key="index2+index">
          <b-form-select v-model="selectedValue[index+index2]" :options="options">
            <template v-slot:first>
              <b-form-select-option :value="null" disabled>Ignore</b-form-select-option>
            </template>
          </b-form-select>
        </th>
      </tr>
    </thead>
    <tbody>
      <tr v-for="(row, index2) in file" :key="index2">
        <td v-for="(data, index3) in row" :key="index3">
          {{ data }}
        </td>
      </tr>
    </tbody>
    

    data: {
    selectedValue: [],
    mappedColumns: [],
    selected: null,
    options: [{
        value: '1',
        text: 'Option 1'
      },
      {
        value: '2',
        text: 'Option 2'
      },
      {
        value: '3',
        text: 'Option 3'
      }
    ],
    

    您在v-model 中为所有下拉菜单使用单个值。所以,一旦你改变了一个下拉菜单。都变了。

    尝试上述解决方案,我在data 中声明了一个新数组selectedValue 您可以将选择的下拉列表的数据保留在此数组中

    【讨论】:

      【解决方案2】:

      我猜你可以把它做成一个组件

      标题:

      可能的用法:

      <thead
        is="THeadSelect"
        :options="header_row"
        :length="length /*defaults to options.length*/"
        :headers.sync="headers"
      ></thead>
      

      组件选项

      //*.js
      const THeadSelect = {
        template: "#theadselect",
        mounted(){
          // make sure headers is populated
          this.$emit("update:headers",
           this.headers
           .concat(Array.from({length:this.length_}, _=>""))
           .slice()
          )
        },
        props: {
          options: {
            type: Array,
            required: true,
          },
          length: Number,
          headers: {
            type: Array,
            required: true
          }
        },
        computed:{
          length_:{
            get(){
              return this.length || this.options.length
            },
            set(l){
              this.$emit("update:length",l)
            }
          },
          filteredOptions(){
            return this.options.filter(
              option => !this.headers.includes(option)
            )
          }
        }
      }
      

      模板

      // *.html 
      <template id="theadselect">
        <thead>
          <tr>
            <th
              v-for="(i,index) in length_"
              :key="index"
            >
              <select 
                v-model="headers[index]">
                <option disabled value="">
                  Please select one
                </option>
      
                <option
                  v-if="headers[index]"
                  selected
                >
                  {{headers[index]}}
                </option>
                <option
                 v-for="option in filteredOptions"
                 :key="option"
                >
                  {{option}}
                </option>
              </select>
            </th>
          </tr>
        </thead>
      </template>
      

      示例

      const THeadSelect = {
        template: "#theadselect",
        mounted(){
          // make sure headers is populated
          this.$emit("update:headers",
           this.headers
           .concat(Array.from({length:this.length_}, _=>""))
           .slice()
          )
        },
        props: {
          options: {
            type: Array,
            required: true,
          },
          length: Number,
          headers: {
            type: Array,
            required: true
          }
        },
        computed:{
          length_:{
            get(){
              return this.length || this.options.length
            },
            set(l){
              this.$emit("update:length",l)
            }
          },
          filteredOptions(){
            return this.options.filter(
              option => !this.headers.includes(option)
            )
          }
        }
      }
      
      new Vue({
      
        components: {THeadSelect},
        data(){
          return {
            headers: [],
            length: 10
          }
        },
        template: "#root"
      }).$mount('#app')
      <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
      <template id="theadselect">
        <thead>
          <tr>
            <th
              v-for="(i,index) in length_"
              :key="index"
            >
              <select 
                v-model="headers[index]">
                <option value="">
                  Please select one
                </option>
                <option
                  v-if="headers[index]"
                  selected
                >
                  {{headers[index]}}
                </option>
                <option
                 v-for="option in filteredOptions"
                 :key="option"
                >
                  {{option}}
                </option>
              </select>
            </th>
          </tr>
        </thead>
      </template>
      
      <template id="root">
        <div>
        <table>
          <caption>Sample usage with select</caption>
          <thead 
            is="THeadSelect"
            :options="['option1', 'option2', 'option3']"
            :headers.sync="headers"
          ></thead>
          <tbody>
            <tr>
              <td
                v-for="(prop, index) in headers"
                :key="prop+index"
              >
                {{ prop || '?'}}
              </td>
            </tr>
          </tbody>
        </table>
        </div>
      </template>
      <table id="app"></table>

      对于正文,可以考虑 headers 数组的值。可能会放置数组索引或对象属性,而不是当前的选项值

      所以对于多个表可以考虑:

      <template id="table">
        <table
      
          v-for="(table, index) in tables"
          :key="'table-'+index"
          is="TableSelect"
          :headers="table[0]"
          :rows="table.slice(1)"
        >
        </table>
      </template>
      

      对于 TableSelect:

      const TableSelect = {
        props: ["headers", "rows"],
        template: "#table-select",
        data(){
          return {
            selectedHeaders: []
          }
        },
        computed(){
          mappedRows(){
            return this.rows
            .map(row=> row.map(
              (cell, index) => ({[headers[index]]: cell})
            ).reduce((obj, val) => Object.assign(obj, val))
          )}
        }
      }
      
      <template id="table-select">
        <table>
          <thead
            is="THeadSelect"
            :options="headers"
            :headers.sync="selectedHeaders"
          ></thead>
          <tbody>
            <tr
              v-for="(row, index) in mappedRows"
              :key="'row-'+index"
            >
              <td
                v-for="cell in selectedHeaders"
                :key="cell+index"
              >
                {{cell && row[cell || ""]}}
              </td>
            </tr>
          </tbody>
        </table>
      </template>
      

      上面的代码有错误,但由于懒惰和缺少 linter,所以我会顺其自然 - 因为它提供了基本思想。


      还有一个关于代码沙盒的运行示例:

      https://lbm8l.csb.app/

      【讨论】:

      • 感谢您的回答,只是通过它以确保我完全理解它。一个问题,您有一个我并不真正需要的删除 col 按钮。我想做的是这个。您提供了选项 1 和选项 2。如果其中一个选项采用这些选项之一,我想将其从其他选项中删除。这样的事情可能吗?
      • 我更新了我原来的帖子。试图适应当前的设置以及我已有的数据似乎会导致问题。
      【解决方案3】:

      使用v-on:change和一个函数代替v-model这里是单独选择的解决方案

      new Vue({
        el: "#app",
        data: {
          mappedColumns: [],
          selected: null,
          options: [{
              value: '1',
              text: 'Option 1'
            },
            {
              value: '2',
              text: 'Option 2'
            },
            {
              value: '3',
              text: 'Option 3'
            }
          ],
          fileData: [
            [
              ["123", "21/11/2013", "Data", "Data"],
              ["234", "22/11/2013", "Data", "Data"],
              ["345", "12/09/2018", "Data", "Data"],
            ],
            [
              ["123", "Data", "Data", "Data", "Data", "Data", "Data", "Data", "Data", "Data",
                "Data"
              ],
              ["234", "Data", "Data", "Data", "Data", "Data", "Data", "Data", "Data", "Data",
                "Data"
              ],
              ["345", "Data", "Data", "Data", "Data", "Data", "Data", "Data", "Data", "Data",
                "Data"
              ]
            ]
          ]
        },
        methods: {
          getSelectedItem(a, b, c) {
            console.log(a, b, c);
          }
        }
      })
      <!DOCTYPE html>
      <html lang="en">
      
      <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <link rel="stylesheet" href="https://unpkg.com/tether@1.4.7/dist/css/tether.min.css">
        <link rel="stylesheet" href="https://unpkg.com/bootstrap-vue@2.15.0/dist/bootstrap-vue.css">
        <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css">
        <script src="https://unpkg.com/vue@2.6.11/dist/vue.js"></script>
        <script src="https://unpkg.com/tether@1.4.7/dist/js/tether.min.js"></script>
        <script src="https://unpkg.com/bootstrap-vue@2.15.0/dist/bootstrap-vue.js"></script>
        <title>Document</title>
        <style>
          #app {
            padding: 20px;
            height: 500px;
          }
        </style>
      </head>
      
      <body>
        <div id='app'>
          <table class="table table-bordered" v-for="(file, index) in fileData" :key="index">
            <thead>
              <tr>
                <th scope="col" v-for="(col, index2) in file[index]" :key="index2">
                  <b-form-select v-on:change="getSelectedItem($event,index,index2)" :options="options">
                    <template v-slot:first>
                                      <b-form-select-option :value="null" disabled>Ignore</b-form-select-option>
                                  </template>
                  </b-form-select>
                </th>
              </tr>
            </thead>
            <tbody>
              <tr v-for="(row, index2) in file" :key="index2">
                <td v-for="(data, index3) in row" :key="index3">
                  {{ data }}
                </td>
              </tr>
            </tbody>
          </table>
        </div>
        <script src="table.js"></script>
      
      </body>
      
      </html>

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-10-14
        • 1970-01-01
        • 1970-01-01
        • 2019-08-29
        • 1970-01-01
        • 1970-01-01
        • 2023-03-30
        • 2011-02-12
        相关资源
        最近更新 更多