Добавить несколько строк в таблицу и вложенную строку в одну строку - PullRequest
1 голос
/ 05 августа 2020

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

HTML:

<button class="addRow"> 
    Add New Row
</button> 

<table> 
    <thead> 
        <tr> 
            <th>Rows</th> 
        </tr> 
    </thead> 
    <tbody> 
        <tr> 
            <td>Row 0</td> 
        </tr> 
    </tbody> 
</table> 

Script :

<script> 
    var lineNo = 1; 
    var html = ""; 

    $(document).ready(function () { 
        $(".addRow").click(function () { 
            html = "<tr><td>Row" + lineNo + "</td></tr>"; //Binding line no 
            tableBody = $("table tbody"); //Getting table element with selector 
            tableBody.append(html); //Appending the table row 
            lineNo++; //Counter to keep track of added rows
        }); 
    });  
</script>

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

product size color price //Column

product1 size1 color1 price1 //Row
product2 size2 color2 price2 

В моем случае мне нужно сделать следующее:

product size color price //Column

product1 size1 color1(+) price1 //Row
product2 size2 color2(+) price2
               color2(-)
               color2(-)
               color2(-)
product3 size3 color3(+) price3
product4 size4 color4(+) price4
               color4(-)
               color4(-)
               color4(-)

Будет добавлена ​​опция добавления строки (+) в вновь созданная строка, чтобы пользователь мог добавлять или удалять строки и требовать сохранения отслеживания, скажем, product1 с несколькими цветами будет назначен этой спецификации c product 1 и так далее. Я жду идей для эффективной реализации этой функции и с нетерпением жду отзывов экспертов.

1 Ответ

2 голосов
/ 05 августа 2020

Я создаю сниппет, который может добавлять любое количество строк (без защиты от ошибок):

// initial data
// container for keepeng items
let data = [{
    id: 0,
    size: 10
  },
  {
    id: 1,
    size: 12
  },
]

// START: DOM manipulation functions
const cellTemplate = (cell) => {
  return `<td>${ cell }</td>`
}

const rowTemplate = (row) => {
  let html = ''
  html += '<tr>'
  Object.entries(row).forEach(([key, value]) => {
    html += cellTemplate(value)
  })
  html += `<td><button class="removeRow" data-rowid=${ row.id }>Remove</button></td>`
  html += '</tr>'
  return html
}

const tbodyTemplate = (data) => {
  let html = ''
  data.forEach(e => {
    html += rowTemplate(e)
  })
  return html
}

const updateTbody = (data) => {
  jQuery("tbody").html(tbodyTemplate(data))
}

// END: DOM manipulation functions

// calculating new ID (very sloppy, but it's OK for a snippet)
const nextId = (data) => {
  const nextIdx = Math.max.apply(null, data.map(({ id }) => id)) + 1
  return nextIdx < 0 ? 0 : nextIdx
}

// adding ANY number of items to the
// item container (data)
const addItem = (data, number) => {
  const d = JSON.parse(JSON.stringify(data))
  for (let i = 0; i < number; i++) {
    d.push({
      id: nextId(d),
      size: 9 + i
    })
  }
  return d
}

// init view
jQuery(document).ready(function($) {
  updateTbody(data)

  // add one row
  $('.addRow').on('click', function() {
    data = addItem(data, 1)
    updateTbody(data) // updating DOM with the new dataset
  })

  // add number of rows based on input
  $('.addMoreRows').on('click', function() {
    const numRows = $('#rowNum').val()
    data = addItem(data, numRows)
    updateTbody(data) // updating DOM with the new dataset
  })

  // remove one row
  // watch out for the dynamic binding!
  $('body').on('click', '.removeRow', function() {
    const id = Number($(this).attr("data-rowid"))
    const idx = data.indexOf(data.find(e => e.id === id))
    data.splice(idx, 1)
    updateTbody(data) // updating DOM with the new dataset
  })
})
table {
  border-collapse: collapse;
}

table,
tr,
th,
td {
  border: 1px solid black;
}

thead {
  background: gray;
  border-bottom: 2px solid black;
}

tbody tr:nth-child( 2n) {
  background: lightgray
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button class="addRow"> 
    Add New Row
</button><br />
<button class="addMoreRows"> 
    Add New Rows
</button>
<label for="rowNum">How many rows do you want to add?<input id="rowNum" type="number" value=1 min=1 max=10 /></label>

<table>
  <thead>
    <tr>
      <th>ID</th>
      <th>Size</th>
      <th>Manage</th>
    </tr>
  </thead>
  <tbody>
  </tbody>
</table>

Основная идея состоит в том, что ничто не отслеживает количество элементов (явно), но все основано на «одном источнике истины»: массив data (объектов). Если вы манипулируете этим массивом и обновляете DOM на основе управляемого массива, тогда таблица изменяется соответствующим образом.

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

Вы можете создавать подэлементы, подподпункты элементы и функции, которые удаляют только один подпункт или основной элемент целиком. Лог c прост: только модифицируйте данные, а затем обновите DOM на основе шаблонов. (Это означает: сначала создайте структуру данных, функции, которые могут управлять этой структурой, как вам нужно, и только затем оберните ее в таблицу с кнопками.)

UPDATE

1. Создайте структуру данных, которая представляет ваш случай

let data = [{
    id: 0,
    size: 10,
    color: "red",
    price: 12,
    variants: [{
        size: 12
      },
      {
        size: 14
      }
    ]
  },
  {
    id: 1,
    size: 10,
    color: "blue",
    price: 15,
    variants: [{
        size: 12
      },
      {
        size: 14
      }
    ]
  }
]

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

2. Создайте функции, которые изменяют (мутируют) структуру:

let data = [{
    id: 0,
    size: 10,
    color: "red",
    price: 12,
    variants: [{
        id: "0_1",
        size: 12
      },
      {
        id: "0_2",
        size: 14
      }
    ]
  },
  {
    id: 1,
    size: 10,
    color: "blue",
    price: 15,
    variants: [{
        id: "1_1",
        size: 12
      },
      {
        id: "1_2",
        size: 14
      }
    ]
  }
]

// mocking a new item
const newItem = {
  id: 2,
  size: 10,
  color: "orange",
  price: 8,
  variants: [{
      id: "2_1",
      size: 12
    },
    {
      id: "2_2",
      size: 14
    }
  ]
}

const addItem = (data, newItem) => {
  let d = JSON.parse(JSON.stringify(data))
  d = [...d, newItem]
  return d
}

const addSubItem = (data, itemId, subItem) => {
  let d = JSON.parse(JSON.stringify(data))
  let item = d.find(({
    id
  }) => id === itemId)
  item.variants = [...item.variants, subItem]
  return d
}

const removeItem = (data, itemId) => {
  let d = JSON.parse(JSON.stringify(data))
  const idx = d.indexOf(d.find(({ id }) => id == itemId))
  if (idx !== -1) {
    d.splice(idx, 1)
  }
  return d
}

const removeSubItem = (data, subItemId) => {
  let d = JSON.parse(JSON.stringify(data))
  d.forEach(({ variants }) => {
    const variant = variants.find(({ id }) => id === subItemId)
    if (variant) {
      const idx = variants.indexOf(variant)
      if (idx !== -1) {
        variants.splice(idx, 1)
      }
    }
  })
  return d
}

// original data
console.log('original data:', data)

// adding item
data = addItem(data, newItem)
console.log('after adding new item:', data)

// adding sub-item
data = addSubItem(data, 1, {
  id: "1_3",
  size: 15
})
console.log('after adding new sub-item:', data)

// removing item
data = removeItem(data, 0)
console.log('after removing item:', data)

// removing sub-item
data = removeSubItem(data, "2_1")
console.log('after removing sub-item:', data)

Хорошо, теперь у нас есть четыре функции для изменения данных:

  • addItem
  • addSubItem
  • removeItem
  • removeSubItem

3. Оберните его в HTML

let data = [{
    id: 0,
    size: 10,
    color: "red",
    price: 12,
    variants: [{
        id: "0_1",
        size: 12
      },
      {
        id: "0_2",
        size: 14
      }
    ]
  },
  {
    id: 1,
    size: 10,
    color: "blue",
    price: 15,
    variants: [{
        id: "1_1",
        size: 12
      },
      {
        id: "1_2",
        size: 14
      },
      {
        id: "1_2",
        color: "beige"
      }
    ]
  }
]

const headers = ["id", "size", "color", "price"]

const createTableArray = (data, headers) => {
  const items = data.map(item => {
    const itemRow = headers.map(header => {
      return item[header]
    })
    const subItemRows = item.variants.map(variant => {
      return headers.map(header => {
        return variant[header] || ''
      })
    })
    return [itemRow, ...subItemRows]
  })
  return [headers, ...items]
}

const tableTemplate = (tableArray) => {
  let html = ''
  tableArray.forEach((e, i, d) => {
    let row = ''
    if (!i) {
      row += '<tr>'
      e.forEach(attrs => {
        row += `<th>${ attrs }</th>`
      })
        row += `<th>manage</th>`
      row += '</tr>'
    } else {
      e.forEach(attrs => {
        row += `<tr>`
        attrs.forEach(attr => {
          row += `<td>${ attr }</td>`
        })
        row += `<td><button class="removeItem" data-itemid="${ attrs[0] }">REMOVE</button></td>`
        row += '</tr>'
      })
    }
    html += row
  })
  return html
}

const updateTable = (tableArray) => {
  document.querySelector('table').innerHTML = tableTemplate(tableArray)
}

const tableArray = createTableArray(data, headers)
updateTable(tableArray)

// mocking a new item
const newItem = {
  id: 2,
  size: 10,
  color: "orange",
  price: 8,
  variants: [{
      id: "2_1",
      size: 12
    },
    {
      id: "2_2",
      size: 14
    }
  ]
}

const addItem = (data, newItem) => {
  let d = JSON.parse(JSON.stringify(data))
  d = [...d, newItem]
  return d
}

const addSubItem = (data, itemId, subItem) => {
  let d = JSON.parse(JSON.stringify(data))
  let item = d.find(({ id }) => id === itemId)
  item.variants = [...item.variants, subItem]
  return d
}

const removeItem = (data, itemId) => {
  let d = JSON.parse(JSON.stringify(data))
  const idx = d.indexOf(d.find(({ id }) => id == itemId))
  if (idx !== -1) {
    d.splice(idx, 1)
  }
  return d
}

const removeSubItem = (data, subItemId) => {
  let d = JSON.parse(JSON.stringify(data))
  d.forEach(({ variants }) => {
    const variant = variants.find(({ id }) => id === subItemId)
    if (variant) {
      const idx = variants.indexOf(variant)
      if (idx !== -1) {
        variants.splice(idx, 1)
      }
    }
  })
  return d
}

jQuery(document).ready(function($) {
  $("#addItem").on('click', function() {
    data = addItem(data, newItem)
    updateTable(createTableArray(data, headers))
  })
  $("body").on('click', '.removeItem', function() {
    const itemId = $(this).data("itemid")
    const before = data.length
    data = removeItem(data, itemId)
    if (before === data.length) {
      data = removeSubItem(data, itemId)
    }
    updateTable(createTableArray(data, headers))
  })
})
table {
  border-collapse: collapse;
}

table, tr, td, th {
  border: 1px solid black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button id="addItem">ADD ONE ITEM</button><br />
<table>
</table>

Теперь мы там:

  • структура данных готова
  • данные могут быть изменены ( элементы и вложенные элементы добавлены и удалены)
  • Пользовательский интерфейс (HTML) на месте (добавление одного элемента, удаление вложенных элементов по строке, удаление элементов вместе с его вложенными элементами)

Есть некоторые проблемы со сниппетом (ID строк связан с позицией ID в массиве), никакие подпункты (варианты) не могут быть добавлены через UI. Но я думаю, что если немного поработать, его можно будет модифицировать, чтобы сделать все, что вам нужно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...