Expandable Row w/Nested Grid

3 of 122
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
[[record.name]] ([[record.job]])

Full Code

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

<head>
  <meta charset="utf-8">
  <title>ZingGrid Demo</title>
  <script src="https://cdn.zinggrid.com/zinggrid.min.js" defer></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,
    .expandable {
      line-height: 1.5rem;
      padding: 0.9rem 1rem;
    }

    .zg-body .default--arrow {
      padding: 0.9rem 0;
    }

    zing-grid[loading] {
      height: 481px;
    }
  </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">
        <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-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>
          </zing-grid>
        </div>
      </zg-column>
    </zg-colgroup>
  </zing-grid>
  <script>
    function assignDataToNestedGrid(index, cellRef, recordRef) {
      console.log(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
      setTimeout(function() {
        const zgFirstRow = document.querySelector('zg-row[aria-rowindex="1"]');
        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" defer></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">
           <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-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>
              </zing-grid>
           </div>
         </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, .expandable { line-height:1.5rem; padding:0.9rem 1rem; }
.zg-body .default--arrow { padding:0.9rem 0; }
function assignDataToNestedGrid(index, cellRef, recordRef) {
  console.log(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
  setTimeout(function() {
    const zgFirstRow = document.querySelector('zg-row[aria-rowindex="1"]');
    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