Not able to display data streaming from websocket to dataTable

Viewed 709

I am trying to stream data via a websocket & display it in dataTable. I am able to get data in my client but no data is actually getting displayed in the table.

How can I display the updated data inplace in realtime in the dataTables as soon as the websocket data is streamed.

    <script type="text/javascript" charset="utf-8">
        streamData = ""
        streamDataArr = new Array();
            $(document).ready(function() {
                namespace = 'http://localhost:8080/';
                var socket = io(namespace);
                socket.on('connect', function() {
                    socket.emit('my_event', {data: 'connected to the SocketServer...'});
                });
                socket.on('my_response', function(msg, cb) {
                    streamData = JSON.parse(msg.data);
                    $('#log').append('<br>' + $('<div/>').text('logs message :- ' + JSON.stringify(streamData)).html());
                    streamDataArr.push([streamData.instrument_token, streamData.last_price, streamData.ohlc.open])
                    console.log(streamDataArr); //value is present
                });         
            });
    
        $(document).ready(function() 
        {
            alert("Arr:- ",streamDataArr) //empty why ?
            alert("soemthing", streamDataArr[0]) //empty why ?
            var table = $('#example').DataTable(
                {
            data: streamDataArr,
            columns: [
                { title: "Name" },
                { title: "last price" },
                { title: "Open price" },
                    ]
                } 
                                               );
        });
        </script>

<body>
    <table id="example" class="display" style="width:100%"> </table>   
</body>

P.S. i am new to datatable & socket-io

Edit:- v2 of the code.

 <script type="text/javascript" charset="utf-8">
    streamData = ""
    streamDataArr = new Array();
       

    
    function fillDataTable(dataSet){
    {
        if (document.readyState === "complete"){
        //alert("Arr:- ",dataSet) //empty why ?
        //alert("soemthing", dataSet[0]) //empty why ?
        var table = $('#example').DataTable(
            {
        data: dataSet,
        retrieve: true,
        deferRender: true,
        searching: false,
        paging: false,
        columns: [
            { title: "stock Name" },
            { title: "stock last price" },
            { title: "stock Open price" },
                ]
            });
    
            }
        }
    }

            namespace = 'http://localhost:8080/';
            var socket = io(namespace);

            socket.on('my_response', function(msg, cb) {
                streamData = JSON.parse(msg.data);
                $('#log').append('<br>' + $('<div/>').text('logs message :- ' + JSON.stringify(streamData)).html());
                streamDataArr.push([streamData.instrument_token, streamData.last_price, streamData.ohlc.open])
                console.log(streamDataArr); //value is present
                fillDataTable(streamDataArr);
                
                $('#example').DataTable().row.add( [streamData.instrument_token, streamData.last_price, streamData.ohlc.open] ).draw();
                redraw();
                // $('#example').DataTable().row(streamData.instrument_token).data("stock last price")  =  streamData.last_price
                // $('#example').DataTable().row(streamData.instrument_token).data("stock Open price")  =  streamData.ohlc.open
                
            });     
            function redraw(){  
                var table = $('#example').DataTable();
                var index = table.column( 0 ).data().indexOf( streamData.instrument_token );
                if (index != ""){
                alert("index is", String(index))
                console.log(String(index))
                $('#example').DataTable().row(index).data( [streamData.instrument_token, streamData.last_price, streamData.ohlc.open] ).draw();
                }
            }
    </script>

By this the 1 entry is appearing twice, I cant really test, whether it updates in place as the source websocket is down

v3 version. By this the data is streaming but I am having duplicate entries as shown in the pic. I also noticed that it is replacing entries with other stocks.

enter image description here

       <script type="text/javascript" charset="utf-8">    
        $(document).ready(function () {
    var table; 
    
    // a dummy initial row for testing:
    var dataSet = [  ];
    $(document).ready(function () {
        table = $('#example').DataTable({
            data: dataSet,
            columns: [
                {title: "Symbol", data: "instrument_token"},
                {title: "StockName", data: "stockName"},
                {title: "last_price", data: "last_price"},
                {title: "ohlcOpen", data: "ohlc.open"}
                
            ]
        });
    });
    
    // small helper function for selecting element by id
    let id = id => document.getElementById(id);
    
    //Establish the WebSocket connection and set up event handlers
    let ws = io.connect(null, {port: 8000, rememberTransport: false});
                ws.on('my_response', function(msg, cb) {
                    updateTable(msg);
                });
    //ws.onmessage = msg => updateTable(msg);
    ws.onclose = () => alert("WebSocket connection closed");
        
    function updateTable(message) {
        let stockData = JSON.parse(message.data);        
        console.log("in updateTable stock data:-", stockData)
        // check if symbol already in table:
        var index = table.column( 0 ).data().indexOf( stockData.instrument_token );
        
        if (index >= 0) {
            // update the existing row:
            table.row( index ).data( stockData ).draw();
        } else {
            // insert a new row:
            table.row.add( stockData ).draw();
        }
    }
    
    });
</script>
1 Answers

I have an example DataTable receiving data from a websocket source - but it is the standard JavaScript WebSocket library - not socket.io.

However, I don't think that is an issue here, because the approach to displaying data in the Datatable is based on using the DataTables API with the source data returned from the websocket - and you already have that data: console.log(streamDataArr); //value is present.

In my test example, I have a simple table which contains one initial record sourced from a local variable:

var dataSet = [ { "msg": "started" } ];
$(document).ready(function () {
  $('#example').DataTable({
    data: dataSet,
    columns: [{title: "Message", data: "msg"}]
  });
});

My websocket data provides simple strings in my case (it could be JSON or anything you want, of course). I take that string and add it to the DataTable using the DataTables API. I do that in the socket handler, where the data is available:

var socketOutputText = [the data from the socket];
var newRow = { "msg": socketOutputText  };
$('#example').DataTable().row.add( newRow ).draw();

Here, I create a JS object newRow containing the fields needed for a row of DataTable data. In my case there is only one field (the table only has 1 column). I use the data name of msg to match the name I defined in my DataTable.

The I use the DataTables row.add() API function to add the row to the table and force a re-display of the table.

This is crude - it just keeps appending new rows to the table.

You can enhance this by adding logic to check for the existence of a record in the table (e.g. find a row key) and then update an existing row. So, the socket data could be a stock ticker feed, containing a symbol and a price.

You can use the indexOf() API call to locate the existing row for your stock symbol, Then you can use the row().data( newRow ) API call to update an existing row in the table.

With this approach, you need to place all your socket processing inside a "document ready" function, to ensure that your code does not attempt to interact with a Datatable which has not yet been initialized.


Here is the basic web page. I use this to display the DataTable - but I also use it to feed in my ticker data (just as way to test):

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Webs Sockets</title>

        <script src="https://code.jquery.com/jquery-3.5.1.js"></script>
        <script src="https://cdn.datatables.net/1.10.22/js/jquery.dataTables.js"></script>
        <link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.22/css/jquery.dataTables.css">
        <link rel="stylesheet" type="text/css" href="https://datatables.net/media/css/site-examples.css">

        <link rel="stylesheet" href="style.css">
    </head>
    <body>

        <table id="example" class="display dataTable cell-border" style="width:100%"></table>

        <br><br>

        <div id="tickControls">
            <input id="message" placeholder="Enter data as JSON string">
            <button id="send">Send</button>
        </div>
        
        <div id="tick"></div>
        <script src="websocketDemo.js"></script>


    </body>
</html>

The websocketDemo.js used in the above page:


$(document).ready(function () {

    var table; 
    
    // a dummy initial row for testing:
    var dataSet = [ {"symbol": "AAPL", "price": 134.28} ];
    $(document).ready(function () {
        table = $('#example').DataTable({
            data: dataSet,
            columns: [
                {title: "Symbol", data: "symbol"},
                {title: "Price", data: "price"}
            ]
        });
    });

    // small helper function for selecting element by id
    let id = id => document.getElementById(id);

    //Establish the WebSocket connection and set up event handlers
    let ws = new WebSocket("ws://" + location.hostname + ":" + location.port + "/ticker");
    ws.onmessage = msg => updateTable(msg);
    ws.onclose = () => alert("WebSocket connection closed");

    // Add event listeners to button - this is just used to provide test input data
    id("send").addEventListener("click", () => sendAndClear(id("message").value));

    function sendAndClear(message) {
        if (message !== "") {
            ws.send(message);
            id("message").value = "";
        }
    }

    function updateTable(message) {
        let stockData = JSON.parse(message.data);        
        // check if symbol already in table:
        var index = table.column( 0 ).data().indexOf( stockData.symbol );
        
        if (index >= 0) {
            // update the existing row:
            table.row( index ).data( stockData ).draw();
        } else {
            // insert a new row:
            table.row.add( stockData ).draw();
        }

    }

});

Again, bear in mind that this handles input test data, as well as updating the DataTable. Comments included in the code.

The page looks like this:

enter image description here

To enter a new ticker price, I enter a piece of JSON into the input field. In the above screenshot you can see the results of entering these two separate ticker updates:

{"symbol": "MSFT", "price": 258.82}

and

{"symbol": "AAPL", "price": 133.97}

You mentioned that you handle multiple symbols at a time - so my basic demo would have to loop through the JSON to handle each one separately in my updateTable() function.

Related