Верста вложенной таблицы с данными третьего уровня вложенности

Spread the love

Хочу в этой статье рассказать о вроде бы простой задаче, верстки таблицы с иерархическими данными.

Раскрутка сайта недвижимости: стратегия от TurboSEO

Продвижение seo сайтов недвижимости.
Какие методы следует применять при раскрутке сайта недвижимости?
Как оптимизировать сайт риэлтера?
Что необходимо делать, чтобы привлечь от 300-500 уников в сутки?
Подробно в статье https://turboseo.net.ua/blogue/prodvizhenie-sajta-nedvizhimosti.htm

Продвижение сайтов недвижимости строится по определенной стратегии. Она включает в себя: сбор семантического ядра, основываясь на ключевые запросы в поисковике, проработка структуры с созданием разделов и, если необходимо, подразделов, проставлением мета-тегов, написанием статей и обязательным учетом технических нюансов….

Все примеры приведенные в этой статье реализованы во Vue.js, но при необходимости их можно адаптировать на любой фреймворк. Так для AngularJS вместо template можно использовать теги ng-repeat-start/ng-repeat-end.

Итак пусть у нас есть исходные иерархические данные для таблицы.

C данными имеющими двойную вложенность иерархии, как правило проблем не возникает.
Допустим нам нужно получить такую таблицу:

Из вот таких исходных данных:

objects = [{
      attr1: 'Id 1',
      attr2: [{
        attr3: 'Value 1'
        }, {
        attr3: 'Value 2'
     }]
    }, {
    	attr1: 'Id 2',
        attr2: [{
           attr3: 'Value 1'
        }, {
           attr3: 'Value 2'
     }]
    }]

Тут все просто, нужно пройтись по каждому элементу массива attr2 и отобразить их в виде строки через тег tr. Данные в первой ячейки объединить через rowspan. Так же нужно поставить условие v-if что бы не отобрать лишние ячейки td

Верстка таких данных будет выглядеть следующим образом:

  <table class="table">
    <tbody v-for="obj in objects">
      <tr v-for="subobj, index in obj.attr2">
        <td v-if="index == 0" :rowspan="obj.attr2.length">{{obj.attr1}}</td>
        <td>{{subobj.attr3}}</td>
      </tr>
    </tbody>
  </table>

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

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

В этом случае таблица должна будет выглядеть следующим образом:

Первый способ решения такой задачи является по сути такое же решения как предыдущее только в ячейке Value можно отобразить вложенную таблицу. А потом через CSS растянуть вложенную таблицу и убрать лишние отступы. Такой способ прост и понятен в реализации.

И так пусть наши данные будут иметь следующий вид:

objects: [
      {
        'id': 'Key 1',
        'attr1': [
          {
            'attr2': 'Value 1',
            'attr3': ['a', 'b', 'c']
          },
          {
            'attr2': 'Value 2',
            'attr3': ['d', 'e']}
        ]
      },
      {
        'id': 'Key 2',
        'attr1': [
          {
            'attr2': 'Value 3',
            'attr3': ['f']
          },{
            'attr2': 'Value 4',
            'attr3': ['g', 'h']
          }
        ]
      }
    ]

Тогда верстка будет такой:

<table class="table table-bordered">
    <tbody>
      <tr v-for="obj in objects">
        <td>{{obj.id}}</td>
        <td class="inner">
          <table class="table">
            <tbody>
              <tr v-for="subobj in obj.attr1">
                <td>{{subobj.attr2}}</td>
                <td class="inner">
                  <div v-for="val in subobj.attr3">{{val}}</div>
                </td>
              </tr>
            </tbody>
          </table>
        </td>
      </tr>
    </tbody>

И еще добавим немного стилей для удаления лишних отступов и правки границ ячеек:

table {
  width: 100%;
  border-collapse: collapse;
}

table>tbody>tr>td,
table>tbody>tr>td.inner>div {
  vertical-align: top;
  border: 1px solid #DDD;
  ;
}

table>tbody>tr>td.inner {
  padding: 0;
  border-right: 0;
}

table>tbody>tr>td.inner>div {
  padding: 5px;
  border-width: 0 0 1px 0;
}

table>tbody>tr>td.inner>div:last-child {
  border: 0;
}

table>tbody>tr>td.inner>table {
  margin-bottom: 0;
}

table>tbody>tr>td.inner>table td {
  border-width: 0 1px 1px 0;
}

table>tbody>tr>td.inner>table tr:last-child td {
  border-bottom: 0;
}

table>tbody>tr>td.inner>div {
  border-right: 0;
}

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

И так допусти нам нужно отобразить следующую таблицу:

Исходные данные пусть будут в таком формате:

objects: [
      {
        'id': 'Key 1',
        'attr1': [
          {
            'attr2': 'Value 1',
            'attr3': ['a', 'b', 'c']
          },
          {
            'attr2': 'Value 2',
            'attr3': ['d', 'e']}
        ]
      },
      {
        'id': 'Key 2',
        'attr1': [
          {
            'attr2': 'Value 3',
            'attr3': ['f', 's', 'a']
          },{
            'attr2': 'Value 4',
            'attr3': ['g', 'h']
          }
        ]
      }
    ]

Тогда верстка будет иметь следующий вид:

<table class="table">
    <thead>
      <th>111</th>
      <th>222</th>
      <th>333</th>
    </thead>
    <template v-for="obj in objects">
      <tr>
        <td :rowspan="getRowCount(obj)">{{obj.id}}</td>
        <td :rowspan="obj.attr1[0].attr3.length">{{obj.attr1[0].attr2}}</td>
        <td>{{ obj.attr1[0].attr3[0]}}</td>
      </tr>
      <tr v-for="val2 in obj.attr1[0].attr3.slice(1)">
        <td>{{val2}}</td>
      </tr>
      <template v-for="subkey in obj.attr1.slice(1)">
        <tr>
          <td :rowspan="subkey.attr3.length">{{subkey.attr2}}</td>
          <td>{{ subkey.attr3[0] }}</td>
        </tr>
        <tr v-for="value3 in subkey.attr3.slice(1)">        
          <td>{{ value3 }}</td>
        </tr>
      </template>
    </template>
</table>

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

...
methods: {
  	getRowCount(obj) {
    	    let total = 0;
            obj.attr1.forEach(item => {
      	       total += item.attr3.length;
            });
            return total;
        }
  }
....

Все остальные детали реализации в принципе понятны из самой верстки.

Надеюсь эта статья поможет вам в реализации подобной задачи.

Была ли вам полезна эта статья?
[2 / 5]

Spread the love
Подписаться
Уведомление о
guest
1 Комментарий
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Анонимно
Анонимно
2 лет назад

А как будет выглядеть код для n-ого количества уровней?

Last edited 2 лет назад by Анонимно