Expandable Row with Nested Grid

7 of 124
ZingGrid provides custom HTML, CSS, and JavaScript to easily create a collapsable row with a secondary nested grid in the tree.
Result Full HTML CSS JS
Edit Download

Full Code

<!DOCTYPE html>
<html class="zg-html">

<head>
  <meta charset="utf-8">
  <title>ZingGrid Demo</title>
  <script nonce="undefined" src="https://cdn.zinggrid.com/zinggrid.min.js"></script>
  <style>
    .zg-body {
      background: #fff;
    }

    zing-grid[loading] {
      height: 500px;
    }

    /** Expandable Row Trigger **/
    .zg-body .arrow {
      display: inline-block;
      transform: rotate(90deg);
      transform-origin: 0% 0%;
      cursor: pointer;
      font-size: 1.5rem;
      position: relative;
      left: 2.5rem;
      top: 0.25rem;
    }

    .zg-body .active .arrow {
      transform: rotate(180deg);
      left: 2.5rem;
      top: 1.5rem;
    }

    /** Expandable Row Header **/
    .zg-body .row--header {
      display: flex;
      align-items: center;
    }

    .zg-body .team--info {
      margin-left: 2.5rem;
    }

    /** Expandable Row CSS **/
    .zg-body zg-cell[type="custom"] {
      padding: 0;
    }

    .zg-body .default {
      border-bottom: 1px solid #ebebeb;
    }

    .zg-body zg-row:hover {
      cursor: pointer;
      background-color: #f5f7fa;
      transition: background-color 0.25s ease-in;
    }

    .zg-body .expandable {
      background: #f5f5f5;
      padding: 1rem 0;
      display: none;
      position: relative;
      overflow: hidden;
    }

    .zg-body .active .expandable {
      display: block;
    }

    /** Set padding **/
    /** NOTE: Css looks off here, but it is to fix how it looks on site **/
    .zg-body .default--arrow {
      padding: 0.9rem 0;
    }

    zing-grid[loading] {
      height: 487px;
    }
  </style>
</head>

<body class="zg-body">
  <zing-grid caption="Expandable Rows w/Nested Grid" viewport-stop loading src="https://zinggrid-example-datasets.uc.r.appspot.com/employees">
    <zg-colgroup>
      <zg-column type="custom" header="Employees" renderer="assignDataToNestedGrid">
        <template>
          <div class="row--header">
            <div class="default  default--arrow">
              <span class="arrow">⌃</span>
            </div>
            <div class="team--info">[[record.name]] ([[record.job]])</div>
          </div>
          <div class="expandable">
            <!-- define nested column and types -->
            <zing-grid width="100%" viewport-stop>
              <zg-colgroup>
                <zg-column index="actor"></zg-column>
                <zg-column index="job"></zg-column>
                <zg-column index="age" type="number"></zg-column>
                <zg-column index="hourly_salary" type="currency"></zg-column>
                <zg-column index="origin"></zg-column>
                <zg-column index="home_town"></zg-column>
                <zg-column index="gender"></zg-column>
                <zg-column index="species"></zg-column>
              </zg-colgroup>
            </zing-grid>
          </div>
        </template>
      </zg-column>
    </zg-colgroup>
  </zing-grid>
  <script>
    ZingGrid.setLicense(['26ccbfec16b8be9ee98c7d57bee6e498']);

    function assignDataToNestedGrid(index, cellRef, recordRef) {
      // assign data to secondary zing-grid
      let zgRef = cellRef.querySelector('zing-grid');
      let data = [recordRef.record];
      zgRef.setData(JSON.stringify(data));
    }
    // window:load event for Javascript to run after HTML
    // because this Javascript is injected into the document head
    window.addEventListener('load', () => {
      // Javascript code to execute after DOM content
      const zgRef = document.querySelector('zing-grid');
      zgRef.addEventListener('row:click', bindExpandEvent);

      function bindExpandEvent(e) {
        let oDOMRow = e.detail.ZGTarget;
        oDOMRow.classList.toggle('active');
        // refresh the grid to properly size all the columns    
        oDOMRow.querySelector('zing-grid').refresh();
      }
      // open first row
      zgRef.executeOnLoad(() => {
        setTimeout(function() {
          const zgFirstRow = document.querySelector('zg-row[aria-rowindex="2"]');
          zgFirstRow.classList.toggle('active');
          zgFirstRow.querySelector('zing-grid').refresh();
        }, 200);
      });
    });
  </script>
</body>

</html>
<!DOCTYPE html>
<html class="zg-html">

<head>
  <meta charset="utf-8">
  <title>ZingGrid Demo</title>
  <script src="https://cdn.zinggrid.com/zinggrid.min.js"></script>
</head>

<body class="zg-body">
  <zing-grid caption="Expandable Rows w/Nested Grid" viewport-stop loading
    src="https://zinggrid-example-datasets.uc.r.appspot.com/employees">
    <zg-colgroup>
      <zg-column type="custom" header="Employees" renderer="assignDataToNestedGrid">
        <template>
          <div class="row--header">
            <div class="default  default--arrow">
              <span class="arrow">⌃</span>
            </div>
            <div class="team--info">[[record.name]] ([[record.job]])</div>
          </div>
          <div class="expandable">
            <!-- define nested column and types -->
            <zing-grid width="100%" viewport-stop>
              <zg-colgroup>
                <zg-column index="actor"></zg-column>
                <zg-column index="job"></zg-column>
                <zg-column index="age" type="number"></zg-column>
                <zg-column index="hourly_salary" type="currency"></zg-column>
                <zg-column index="origin"></zg-column>
                <zg-column index="home_town"></zg-column>
                <zg-column index="gender"></zg-column>
                <zg-column index="species"></zg-column>
              </zg-colgroup>
            </zing-grid>
          </div>
        </template>
      </zg-column>
    </zg-colgroup>
  </zing-grid>
</body>

</html>
.zg-body{ background:#fff; }

zing-grid[loading] { height: 500px; }

/** Expandable Row Trigger **/
.zg-body .arrow { display:inline-block; transform:rotate(90deg); transform-origin: 0% 0%; cursor:pointer; font-size:1.5rem; position:relative; left:2.5rem; top:0.25rem; }
.zg-body .active .arrow { transform:rotate(180deg); left:2.5rem; top:1.5rem; }

/** Expandable Row Header **/
.zg-body .row--header { display:flex; align-items:center; }
.zg-body .team--info { margin-left:2.5rem; }

/** Expandable Row CSS **/
.zg-body zg-cell[type="custom"] { padding:0; }
.zg-body .default { border-bottom:1px solid #ebebeb; }
.zg-body zg-row:hover { cursor:pointer; background-color:#f5f7fa; transition:background-color 0.25s ease-in; }
.zg-body .expandable { background:#f5f5f5; padding:1rem 0; display:none; position:relative; overflow:hidden; }
.zg-body .active .expandable { display:block; }

/** Set padding **/
/** NOTE: Css looks off here, but it is to fix how it looks on site **/
.zg-body .default--arrow { padding:0.9rem 0; }
function assignDataToNestedGrid(index, cellRef, recordRef) {
  // assign data to secondary zing-grid
  let zgRef = cellRef.querySelector('zing-grid');
  let data = [recordRef.record];
  zgRef.setData(JSON.stringify(data));
}
// window:load event for Javascript to run after HTML
// because this Javascript is injected into the document head
window.addEventListener('load', () => {
  // Javascript code to execute after DOM content
  const zgRef = document.querySelector('zing-grid');
  zgRef.addEventListener('row:click', bindExpandEvent);

  function bindExpandEvent(e) {
    let oDOMRow = e.detail.ZGTarget;
    oDOMRow.classList.toggle('active');
    // refresh the grid to properly size all the columns    
    oDOMRow.querySelector('zing-grid').refresh();
  }
  // open first row
  zgRef.executeOnLoad(() => {
    setTimeout(function() {
      const zgFirstRow = document.querySelector('zg-row[aria-rowindex="2"]');
      zgFirstRow.classList.toggle('active');
      zgFirstRow.querySelector('zing-grid').refresh();
    }, 200);
  });
});

Interested in this demo? Modify it to your needs in ZingSoft Studio, our testing sandbox. It's free to sign up, and you can come back and edit at any time!

Edit in Studio

Demo Gallery