Разделить детали строки на несколько строк в 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, ...}]

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


person Waar    schedule 11.01.2021    source источник
comment
Если это невозможно, есть ли какой-нибудь другой элемент ускорения, который имеет больше смысла для достижения того, что я хочу? = ›Свернуть.   -  person tao    schedule 12.01.2021


Ответы (2)


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

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

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

Но ... это Интернет. В сети, поскольку она виртуальная, возможно практически все. Когда это не так, вы делаете неправильно ™.

То, что вы хотите, можно достичь, полностью заменив rows, когда строка расширена, используя 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 && (row.expanded = !row.expanded)"
        :class="{parent: row.children}">
      <td>{{row.id}}</td>
      <td>{{row.name}}</td>
    </tr>
  </table>
</div>

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


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

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 && 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/[email protected]"></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>

person tao    schedule 11.01.2021

Один из подходов (который я лично использовал в прошлом) - просто поместить вложенный <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>

person zcoop98    schedule 11.01.2021
comment
Если вы используете вложенные таблицы, вы теряете самую ценную способность таблицы: динамическое изменение размера столбца в зависимости от содержимого. Я имею в виду, что именно по этой причине люди используют таблицы. Вы можете использовать столбцы одинакового размера (но если вы хотите, чтобы у вас были лучшие варианты, чем таблицы), или вы могли бы динамически применять ширину столбцов дочерней таблицы из ширины столбцов родительской таблицы с помощью прослушивателя на resize. Но я бы сказал, что простое добавление классов в строки, чтобы отличать родителей от детей, является значительно более чистым подходом. Я использовал вложенные таблицы. Слишком быстро становится грязно. - person tao; 12.01.2021
comment
Я согласен до такой степени, что, если OP ищет несколько уровней вложенности, он может быстро стать беспорядочным (и сдавленным). Я не согласен с тем, что вы теряете динамическое изменение размера - bootstrap-vue таблицы обрабатывают изменение размера столбцов для каждой таблицы, поэтому я не вижу, как вложенная таблица вообще уменьшает это. - person zcoop98; 12.01.2021
comment
Столбцы вложенной таблицы не выровнены с родительскими столбцами !? Вы теряете это с самого начала. - person tao; 12.01.2021
comment
Полагаю, я вас тогда неправильно понял, в этом вы правы. Я просто не считаю это полезным условием, если данные дочерней строки имеют поля, отличные от полей родительской, что в данном случае верно. - person zcoop98; 12.01.2021
comment
Таблицы следует использовать для табличных данных. Не для верстки. Представьте себе таблицу, показывающую продажи за разные периоды. И когда вы нажимаете на одну, должны отображаться все транзакции этого периода (или, возможно, меньшая единица периода, которая также может быть расширена). Вы, очевидно, захотите, чтобы суммы и количества были согласованы. Если у вас нет табличных данных, вам следует использовать даже не таблицу, а неупорядоченный список. Верно? - person tao; 12.01.2021
comment
Мое намерение состояло в том, чтобы имитировать работу макросов расширения слияния. Учитывая хорошо напечатанную строку данных, щелкните ее, чтобы развернуть и просмотреть ее содержимое. - person Waar; 12.01.2021