ZingChart Stock Trends

106 of 122

This demo shows off the interaction between ZingGrid and ZingChart – a charting library and another one of our products.

Click a row to see the full data in the chart.

Result Full HTML CSS JS
Edit Download

Full Code

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

<head>
  <meta charset="utf-8">
  <title>ZingGrid Stock Ticker!!!</title>
  <script src="https://cdn.zinggrid.com/zinggrid.min.js" defer></script>
  <style>
    .zg-html,
    .zg-body {
      height: 100%;
      width: 100%;
      margin: 0;
      padding: 0;
    }

    .zg-body .component--container {
      display: flex;
    }

    zing-grid {
      opacity: 1;
    }

    zing-grid.loading {
      opacity: 0;
      transition: opacity .3s ease-out;
    }

    zing-grid.loading * {
      opacity: 0;
    }

    zing-grid,
    .zg-body #myChart {
      width: 49%;
    }

    .zg-body #myChart {
      height: 515px;
    }

    zg-row:hover {
      cursor: pointer;
    }

    zg-column {
      min-height: 150px;
    }

    @media screen and (max-width:1024px) {
      .component--container {
        flex-direction: column;
      }
      zing-grid,
      .zg-body #myChart {
        width: 100%;
      }
    }

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

<body class="zg-body">

  <script src="https://cdn.zingchart.com/zingchart.min.js"></script>
  <script>
    ZC.LICENSE = ['09c5964a057c3124ffaa293847e9aef4', 'd41d8cd98f00b204e9800998ecf8427e', 'faa38520146223e92b6b0d2fc89c0dc6'];
  </script>

  <div class="component--container">
    <!-- ZingGrid webcomponent -->
    <zing-grid caption="ZingGrid Stock Ticker!!!" height="350" layout="row" layout-controls="disabled" viewport-stop pager page-size="5" page-size-option="2,5,10" sort src="https://cdn.zinggrid.com/datasets/company-stock-data-snapshot.json" class="loading">
      <zg-colgroup>
        <zg-column index="Symbol" width=110></zg-column>
        <zg-column index="Date" width=105></zg-column>
        <zg-column index="Open" width=110></zg-column>
        <zg-column index="High" width=110></zg-column>
        <zg-column index="Low" width=110></zg-column>
        <zg-column index="Close" width=110></zg-column>
        <zg-column index="CloseTrends" sort="disabled" renderer="_renderLineChart">
          <div class="line-chart--spark"></div>
        </zg-column>
        <zg-column index="AverageVolume" sort="disabled" renderer="_renderBarChart">
          <div class="bar-chart--spark"></div>
        </zg-column>
        <zg-column index="targetExpenditure" width=135></zg-column>
        <zg-column index="Expenditure.R&D, Expenditure.Marketing, Expenditure.Infrastructure" header="Expenditure" sort="disabled" renderer="_renderPieChart">
          <div class="pie-chart--spark"></div>
        </zg-column>
      </zg-colgroup>
    </zing-grid>
    <!-- ZingChart container -->
    <div id="myChart"></div>
  </div>
  <script>
    ZC.LICENSE = ["569d52cefae586f634c54f86dc99e6a9", "b55b025e438fa8a98e32482b5f768ff5"];
    const ZingChartConfig = function(type, series) {
      return {
        type,
        theme: 'spark',
        tooltip: {
          visible: type != 'pie' ? false : true,
        },
        crosshairX: {

        },
        series,
      };
    };

    function _renderLineChart(ctv, cellRef, $cell) {
      let zcRef = cellRef.querySelector('.line-chart--spark');
      let id = `line-chart-${Math.floor(Math.random() * 99999)}`;
      zcRef.setAttribute('id', id);
      let mapValues = ctv.map(function(trend) {
        return trend.Close;
      });
      // render line chart
      zingchart.render({
        id,
        data: ZingChartConfig(
          'line', [{
            values: mapValues
          }]
        ),
        height: 100,
        width: 150,
      });
    }

    function _renderBarChart(ctv, cellRef, $cell) {
      let zcRef = cellRef.querySelector('.bar-chart--spark');
      let id = `bar-chart-${Math.floor(Math.random() * (Number.MAX_SAFE_INTEGER - 100))}`;
      zcRef.setAttribute('id', id);
      let mapValues = ctv.map(function(trend) {
        return Math.floor(trend.AverageVolume);
      });
      // render chart
      zingchart.render({
        id,
        data: ZingChartConfig(
          'bar', [{
            values: mapValues
          }]
        ),
        height: 100,
        width: 150,
      });
    }

    function _renderPieChart(rd, marketing, infrastructure, cellRef, $cell) {
      let zcRef = cellRef.querySelector('.pie-chart--spark');
      let id = `pie-chart-${Math.floor(Math.random() * 99999)}`;
      zcRef.setAttribute('id', id);
      // render chart
      zingchart.render({
        id,
        data: ZingChartConfig(
          'pie', [{
            text: 'R&D',
            values: [rd]
          }, {
            text: 'Infrastructure',
            values: [infrastructure]
          }, {
            text: 'Marketing',
            values: [marketing]
          }]
        ),
        height: 100,
        width: 150,
      });
    }

    function ChartConfig(title, timeSeries) {
      return {
        type: 'line',
        title: {
          text: title || 'Company N/A'
        },
        noData: {
          text: 'Please Click a Grid Row',
          fontSize: 20,
        },
        crosshairX: {
          plotLabel: {
            padding: 10,
            borderRadius: 5,
            text: '%kt<br>Value:<b>%v</b>'
          }
        },
        scaleX: {
          zooming: true,
          transform: {
            type: 'date',
            item: {
              visible: true
            }
          }
        },
        preview: {},
        series: [{
          values: timeSeries || []
        }]
      };
    }

    // parse a date from day-month-year e.g) 15-Apr-16
    // to be 15-3-16 returned in milliseconds
    function parseDate(input) {
      const parts = input.split('-');
      const monthShortNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec', ];
      // get month index
      const monthInt = monthShortNames.findIndex(m => m == parts[1]);

      // return new date in milliseconds y/m/d
      return new Date(20 + parts[2], monthInt, parts[0]).getTime();
    }

    function renderNewStockData(e) {
      let symbol = e.detail.ZGData.data.Symbol;
      let title = e.detail.ZGData.data.Symbol;
      let uri = `https://cdn.zinggrid.com/datasets/${symbol}-stock-data.json`;
      // fetch data
      fetch(uri)
        .then(res => res.json())
        .then(stockData => {
          const mapValues = stockData.map((point) => [parseDate(point.Date), point.Close]);
          // re-render chart for simplicity
          zingchart.exec('myChart', 'setdata', {
            data: ChartConfig(title, mapValues)
          });
        });
    }

    window.addEventListener('load', () => {
      let zgRef = document.querySelector('zing-grid');

      // render initial main chart
      zingchart.render({
        id: 'myChart',
        height: '100%',
        width: '100%',
        data: ChartConfig(), // empty chartconfig
      });

      // on record/row click, display chart
      zgRef.addEventListener('record:click', renderNewStockData);

      zgRef.executeOnLoad(() => {
        zgRef.updateSize();
      });
    });


    // Custom loading class for CSS handling
    const zgLoaded = document.querySelector('zing-grid');
    zgLoaded.addEventListener('grid:ready', () => {
      setTimeout(() => zgLoaded.classList.remove('loading'), 0);
    });
  </script>
</body>

</html>
<!DOCTYPE html>
<html class="zg-html">
	<head>
    <meta charset="utf-8">
    <title>ZingGrid Stock Ticker!!!</title>
    <script src="https://cdn.zinggrid.com/zinggrid.min.js" defer></script>
  </head>
	<body class="zg-body">

  	<script src="https://cdn.zingchart.com/zingchart.min.js"></script>
    <script>ZC.LICENSE = ['09c5964a057c3124ffaa293847e9aef4','d41d8cd98f00b204e9800998ecf8427e', 'faa38520146223e92b6b0d2fc89c0dc6'];</script>

    <div class="component--container">
      <!-- ZingGrid webcomponent -->
      <zing-grid
        caption="ZingGrid Stock Ticker!!!"
        height="350"
        layout="row"
        layout-controls="disabled"
        viewport-stop
        pager
        page-size="5"
        page-size-option="2,5,10"
        sort
        src="https://cdn.zinggrid.com/datasets/company-stock-data-snapshot.json"
        class="loading"
      >
      	<zg-colgroup>
          <zg-column index="Symbol" width=110></zg-column>
          <zg-column index="Date" width=105></zg-column>
          <zg-column index="Open" width=110></zg-column>
          <zg-column index="High" width=110></zg-column>
          <zg-column index="Low" width=110></zg-column>
          <zg-column index="Close" width=110></zg-column>
          <zg-column index="CloseTrends" sort="disabled" renderer="_renderLineChart">
            <div class="line-chart--spark"></div>
          </zg-column>
          <zg-column index="AverageVolume" sort="disabled" renderer="_renderBarChart">
            <div class="bar-chart--spark"></div>
          </zg-column>
          <zg-column index="targetExpenditure" width=135></zg-column>
          <zg-column index="Expenditure.R&D, Expenditure.Marketing, Expenditure.Infrastructure" header="Expenditure" sort="disabled" renderer="_renderPieChart">
            <div class="pie-chart--spark"></div>
          </zg-column>
        </zg-colgroup>
      </zing-grid>
      <!-- ZingChart container -->
      <div id="myChart"></div>
    </div>
	</body>
</html>
.zg-html, .zg-body {
  height: 100%;
  width: 100%;
  margin: 0;
  padding: 0;
}

.zg-body .component--container {
  display: flex;
}

zing-grid {
  opacity: 1;
}
zing-grid.loading { opacity:0; transition:opacity .3s ease-out; }
zing-grid.loading * { opacity:0; }

zing-grid,
.zg-body #myChart {
  width:49%;
}

.zg-body #myChart {
  height: 515px;
}

zg-row:hover {
  cursor:pointer;
}

zg-column {
  min-height: 150px;
}



@media screen and (max-width:1024px) {
  .component--container { flex-direction:column; }
  zing-grid,
	.zg-body #myChart {
  	width:100%;
	}
}
const ZingChartConfig = function(type, series) {
  return {
    type,
    theme: 'spark',
    tooltip: {
      visible: type != 'pie' ? false : true,
    },
    crosshairX:{
      
    },
    series,
  };
};

function _renderLineChart(ctv, cellRef, $cell) {
  let zcRef = cellRef.querySelector('.line-chart--spark');
  let id = `line-chart-${Math.floor(Math.random() * 99999)}`;
  zcRef.setAttribute('id', id);
  let mapValues = ctv.map(function(trend) {
    return trend.Close;
  });
  // render line chart
  zingchart.render({
    id,
    data: ZingChartConfig(
      'line',
      [{
        values:mapValues
      }]
    ),
    height: 100,
    width: 150,
  });
}

function _renderBarChart(ctv, cellRef, $cell) {
  let zcRef = cellRef.querySelector('.bar-chart--spark');
  let id = `bar-chart-${Math.floor(Math.random() * (Number.MAX_SAFE_INTEGER - 100))}`;
  zcRef.setAttribute('id', id);
  let mapValues = ctv.map(function(trend) {
    return Math.floor(trend.AverageVolume);
  });
  // render chart
  zingchart.render({
    id,
    data: ZingChartConfig(
      'bar',
      [{
        values:mapValues
      }]
    ),
    height:100,
    width:150,
  });
}

function _renderPieChart(rd,marketing, infrastructure, cellRef, $cell) {
  let zcRef = cellRef.querySelector('.pie-chart--spark');
  let id = `pie-chart-${Math.floor(Math.random() * 99999)}`;
  zcRef.setAttribute('id', id);
  // render chart
  zingchart.render({
    id,
    data: ZingChartConfig(
      'pie',
      [{
        text:'R&D',
        values:[rd]
      },{
        text:'Infrastructure',
        values:[infrastructure]        
      },{
        text:'Marketing',
        values:[marketing]        
      }]
    ),
    height:100,
    width:150,
  });
}

function ChartConfig(title,timeSeries) {
  return {
    type: 'line',
    title: {
      text: title || 'Company N/A'
    },
    noData: {
      text: 'Please Click a Grid Row',
      fontSize: 20,
    },
    crosshairX:{
    	plotLabel: {
       	padding:10,
        borderRadius:5,
        text: '%kt<br>Value:<b>%v</b>'
      }
    },
    scaleX:{
      zooming : true,
      transform : {
        type : 'date',
        item : {
          visible : true    
        }
      }
    },
    preview:{},
    series: [
      {
        values: timeSeries || []
      }
    ]
  };
}

// parse a date from day-month-year e.g) 15-Apr-16
// to be 15-3-16 returned in milliseconds
function parseDate(input) {
  const parts = input.split('-');
  const monthShortNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun','Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec',];
  // get month index
  const monthInt = monthShortNames.findIndex(m => m == parts[1]);

  // return new date in milliseconds y/m/d
  return new Date(20 + parts[2], monthInt, parts[0]).getTime(); 
}

function renderNewStockData(e) {
  let symbol = e.detail.ZGData.data.Symbol;
  let title = e.detail.ZGData.data.Symbol;
  let uri = `https://cdn.zinggrid.com/datasets/${symbol}-stock-data.json`;
  // fetch data
  fetch(uri)
    .then(res => res.json())
    .then(stockData => {
      const mapValues = stockData.map((point) => [parseDate(point.Date), point.Close]);
      // re-render chart for simplicity
      zingchart.exec('myChart', 'setdata', {
        data: ChartConfig(title,mapValues)
      });
    });
}

window.addEventListener('load', () => {
  let zgRef = document.querySelector('zing-grid');

  // render initial main chart
  zingchart.render({
    id: 'myChart',
 		height: '100%',
    width: '100%',
    data: ChartConfig(), // empty chartconfig
  });

  // on record/row click, display chart
  zgRef.addEventListener('record:click', renderNewStockData);

  zgRef.executeOnLoad(() => {
    zgRef.updateSize();
  });
});


// Custom loading class for CSS handling
const zgLoaded = document.querySelector('zing-grid');
zgLoaded.addEventListener('grid:ready', () => {
  setTimeout(() => zgLoaded.classList.remove('loading'), 0);
});

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