Разделение сведений о строке на несколько строк в bootstrap-vue

#vue.js #bootstrap-vue

#vue.js #bootstrap-vue

Вопрос:

В настоящее время я пишу свое первое приложение с полным стеком. Я использую bootstrap <b-table> для отображения содержимого. Далее row-click я расширяю строку для отображения вложенных данных. Есть ли способ перебирать вложенные данные и отображать их во вложенных строках в родительской b-таблице?

В настоящее время я могу отображать данные, однако они отображаются в одной строке.

component.vue :

 <template>
    <div id="report-table" class="report-table">
        <b-container>
            <b-table striped hover sticky-header="100%" 
            :items="reports" 
            :fields="fields"
            responsive="xl"
            @click="clearRowClick"
            @row-clicked="reports=>$set(reports, '_showDetails', !reports._showDetails)"
            >
                <template slot="row-details" slot-scope="row">
                    <template v-for="(proc, index) in row.item.Processes">
                        <b-tr :key=index>
                            <td>{{ proc.Name }}</td>
                            <td>{{ proc.Id }}</td>
                        </b-tr>
                    </template>
                </template>
            </b-table>
        </b-container>
    </div>
</template>

 

пример

В прикрепленном изображении была нажата нижняя строка. Содержимое отображается в одной строке, но я бы хотел, чтобы это были отдельные строки, поэтому позже я смогу щелкнуть по ним, чтобы отобразить еще больше вложенного содержимого.

пример данных:

 {"_id": <id>, "Hostname": <hostname>, "Address": <address>, "Processes": [{"Name": ApplicationHost, ...}, {"Name": svchost, ...}]
 

Если это невозможно, есть ли какой-нибудь другой элемент начальной загрузки, который имеет больше смысла для достижения того, чего я хочу?

Комментарии:

1. Если это невозможно, есть ли какой-нибудь другой элемент boostrap, который имеет больше смысла для достижения того, чего я хочу? => Свернуть .

Ответ №1:

Чтобы строго ответить на ваш вопрос: нет, <b-table> row-details строка BootstrapVue не может быть расширена более чем на одну строку.

row-details Строка имеет серьезные ограничения:

  • это только одна строка
  • на самом деле это только одна ячейка, которая при использовании colspan расширяется на всю ширину строки (что означает, что вы не можете использовать столбцы таблицы для выравнивания содержимого row-details строки).

Но … это веб. В web, поскольку он виртуальный, возможно практически все. Если это не так, вы делаете неправильно ™.

То, что вы хотите, достижимо путем rows полной замены при расширении строки, использования a computed и объединения дочерних элементов с их родительской строкой, когда родительский элемент находится в расширенном состоянии. Доказательство концепции:

 Vue.config.productionTip = false;
Vue.config.devtools = false;
new Vue({
  el: '#app',
  data: () => ({
    rows: [
      {id: '1', name: 'one', expanded: false, children: [
        {id: '1.1', name: 'one-one'},
        {id: '1.2', name: 'one-two'},
        {id: '1.3', name: 'one-three'}
      ]},
      {id: '2', name: 'two', expanded: false, children: [
        {id: '2.1', name: 'two-one'},
        {id: '2.2', name: 'two-two'},
        {id: '2.3', name: 'two-three'}
      ]}
    ]
  }),
  computed: {
    renderedRows() {
      return [].concat([...this.rows.map(row => row.expanded 
        ? [row].concat(row.children)
        : [row]
       )]).flat()
    }
  }
}) 
 tr.parent { cursor: pointer } 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <table>
    <tr v-for="row in renderedRows" :key="row.id"
        @click="row.children amp;amp; (row.expanded = !row.expanded)"
        :class="{parent: row.children}">
      <td>{{row.id}}</td>
      <td>{{row.name}}</td>
    </tr>
  </table>
</div> 

Пример довольно простой (я не добавлял к нему BootstrapVue и не использовал его фантазии <b-table> ), но он демонстрирует принцип. Примените его к <b-table> ‘s :items .


Можно даже сделать еще один шаг и сделать его рекурсивным, переместив логику расширения в метод:

 new Vue({
  el: '#app',
  data: () => ({
    fields: ['id',  { key: 'expanded', label: ''}, 'name'],
    rows: [{
        id: '1',
        name: 'one',
        expanded: false,
        children: [
          { id: '1.1', name: 'one-one' },
          { id: '1.2', name: 'one-two' },
          {
            id: '1.3',
            name: 'one-three',
            expanded: false,
            children: [
              { id: '1.3.1', name: 'one-three-one' },
              { id: '1.3.2', name: 'one-three-two' }
            ]
          }
        ]
      },
      {
        id: '2',
        name: 'two',
        expanded: false,
        children: [
          { id: '2.1', name: 'two-one' },
          { id: '2.2', name: 'two-two' },
          { id: '2.3', name: 'two-three' }
        ]
      }
    ]
  }),
  computed: {
    items() {
      return [].concat(this.rows.map(row => this.unwrapRow(row))).flat()
    }
  },
  methods: {
    unwrapRow(row) {
      return row.children amp;amp; row.expanded
        ? [row].concat(...row.children.map(child => this.unwrapRow(child)))
        : [row]
    },
    tbodyTrClass(row) {
      return { parent: row.children?.length, child: row.id.includes('.') }
    }
  }
}) 
 .table td:not(:last-child) { width: 80px; }
.table .bi { cursor: pointer }
tr.child {
  background-color: #f5f5f5;
  font-style: italic;
} 
 <link type="text/css" rel="stylesheet" href="//unpkg.com/bootstrap/dist/css/bootstrap.min.css" />
<link type="text/css" rel="stylesheet" href="//unpkg.com/bootstrap-vue@latest/dist/bootstrap-vue.min.css" />
<script src="//cdn.jsdelivr.net/npm/vue@2.6.14"></script>
<script src="//unpkg.com/bootstrap-vue@latest/dist/bootstrap-vue.min.js"></script>
<script src="//unpkg.com/bootstrap-vue@latest/dist/bootstrap-vue-icons.min.js"></script>

<div id="app">
  <b-table :items="items"
           :fields="fields"
           :tbody-tr-class="tbodyTrClass">
    <template #cell(expanded)="{item}">
      <b-icon v-if="item.children"
              :icon="item.expanded ? 'chevron-up' : 'chevron-down'"
              @click="item.expanded = !item.expanded" />
    </template>
  </b-table>
</div> 

Ответ №2:

Один из подходов (который я лично использовал в прошлом) заключается в том, чтобы просто поместить вложенный <b-table> элемент внутри вашего дочернего row-details элемента для дочерних данных, вместо того, чтобы пытаться добавить их во внешнюю таблицу.

Также стоит отметить, что добавление дочерних строк данных во внешнюю таблицу может привести к визуальной путанице, если они не выглядят достаточно отличными от своих родителей.

Пример:

 new Vue({
  el: '#app',
  data() {
    return {
      reports: [{_id: 'ID#1', Hostname: 'Host1', Address: 'Addr1', Processes: [{Name: 'ApplicationHost', Id: '1'}, {Name: 'svchost', Id: '2'}]},
        {_id: 'ID#2', Hostname: 'Host2', Address: 'Addr2', Processes: [{Name: 'ApplicationHost', Id: '3'}, {Name: 'svchost', Id: '4'}]},],
      fields: ['Hostname', 'Address'],
    }
  },
}); 
 <!-- Import Vue and Bootstrap-Vue -->
<link type="text/css" rel="stylesheet" href="//unpkg.com/bootstrap@4/dist/css/bootstrap.min.css" /><link type="text/css" rel="stylesheet" href="//unpkg.com/bootstrap-vue@latest/dist/bootstrap-vue.min.css" /><script src="//unpkg.com/vue@latest/dist/vue.min.js"></script><script src="//unpkg.com/bootstrap-vue@latest/dist/bootstrap-vue.min.js"></script>

<div id="app">
    <b-table
      bordered
      striped
      hover
      :items="reports" 
      :fields="fields" 
      @row-clicked="reports=>$set(reports, '_showDetails', !reports._showDetails)"
    >
      <!-- <b-table> nested inside 'row-details' slot: -->
      <template #row-details="row">
        <b-table
          bordered
          :items="row.item.Processes"
          :fields="['Name', 'Id']"
        ></b-table>
      </template>
    </b-table>
</div> 

Комментарии:

1. Если вы используете вложенные таблицы, вы теряете самую ценную способность таблицы: динамическое изменение размера столбца в зависимости от содержимого. Я имею в виду, именно по этой причине люди используют таблицы. Вы можете либо использовать столбцы одинакового размера (но если вы хотите, чтобы у вас были лучшие варианты, чем таблицы), либо вы можете динамически применять ширину столбцов дочерней таблицы из ширины столбцов родительской таблицы с включенным прослушивателем resize . Но я бы сказал, что простое добавление классов к строкам для отличия родителей от дочерних элементов — это значительно более чистый подход. Я использовал вложенные таблицы. Это становится слишком беспорядочным слишком быстро.

2. Я согласен настолько, что если OP ищет несколько уровней вложенности, то это может быстро запутаться (и сжаться). Я не согласен с тем, что вы теряете динамическое изменение размера — bootstrap-vue таблицы обрабатывают изменение размера столбцов для каждой таблицы, поэтому я не вижу, как вложенная таблица вообще уменьшает это.

3. Столбцы вложенной таблицы не выровнены с родительскими столбцами !? Вы теряете это с самого начала.

4. Полагаю, я вас неправильно понял, вы правы в этом. Я просто не вижу в этом полезного условия, если данные дочерней строки имеют поля, отличные от родительских, что верно в данном случае.

5. Таблицы должны использоваться для табличных данных. Не для макета. Итак, подумайте о таблице, показывающей продажи за разные периоды. И когда вы нажимаете на одну, должны отображаться все транзакции этого периода (или, возможно, меньшая единица периода, которую также можно расширить). Очевидно, вам нужно выровнять суммы и количества. Если у вас нет табличных данных, вам следует использовать даже не таблицу, а неупорядоченный список. Верно?