Compare and update data from an HTML table with data returned via JSON?

6

Problem: I have a table that is populated by data coming from multiple JSON files, so far no problem. The problem is that I need to update the data if the values contained in the JSON are different from what is in the table, but without changing the order of the items.

All this process should preferably be done using AngularJS only.

If a code (symbol) that came in the JSON does not exist in the table I add it, if it already exists I update. At the moment I have only completed the code below:

<!DOCTYPE html>
<html lang="pt-br" ng-app="money">
    <head>
        <meta charset="UTF-8"/>
        <title>BMF&Bovespa</title>
        <link rel="stylesheet" href="assets/css/bootstrap.min.css">
        <script src="assets/lib/angular.min.js"></script>
        <style>
            .green {
                color: green;
            }
            .red {
                color: red;
            }
        </style>
        <script>
            angular.module("money", []);
            angular.module("money").controller("moneyController", function ($scope, $http, $interval) {

                var papeis = ['ABEaV3.SA','BBTG12.SA', 'AGRO3.SA', 'BPAN4.SA', 'BPHA3.SA', 'BRML3.SA', 'BRSR6.SA', 'BTOW3.SA', 'CARD3.SA', 'CIEL3.SA', 'CMIG4.SA', 'CTKA4.SA', 'CTSA3.SA'];

                function consultarCotacao(papeis) {

                    var arr = [];
                    angular.forEach(papeis, function (papel) {
                        var yql = 'select * from yahoo.finance.quotes where symbol in ("' + papel + '")';
                        var api = 'https://query.yahooapis.com/v1/public/yql?q=' + encodeURIComponent(yql);
                        var url = api + '&format=json&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys&callback=';
                        $http({
                            method: 'GET',
                            url: url,
                            timeout: 3000,
                            headers: {'Content-Type': 'json'}
                        }).then(function successCallback(response) {
                            resultado = response.data.query.results.quote;
                            if (resultado.Name !== null) {
                                arr.push(resultado);
                            } else {
                                console.log(resultado.Symbol + ' não foi encontrado!');
                            }
                        }, function errorCallback(response) {
                            console.log('Falha na chamada do recurso.');
                        });
                    });

                    $scope.valores = arr;
                }
                $scope.color = function (valor)
                {
                    if (valor.indexOf("+") !== -1) {
                        return "green";
                    }
                    return "red";
                }
                consultarCotacao(papeis);
                //$interval(consultarCotacao, 5000, 0);
            });
        </script>
    </head>
    <body ng-controller="moneyController">
        <table class="table table-striped">
            <thead>
                <tr>
                    <th>Código</th>
                    <th>Cotação</th>
                    <th>Variação (RS)</th>
                    <th>Variação (%)</th>
                    <th>Maior cotação</th>
                    <th>Menor cotação</th>
                    <th>Abertura</th>
                    <th>Fechamento</th>
                    <th>Volume</th>
                </tr>
            </thead>
            <tbody>
                <tr ng-repeat="valor in valores">
                    <td>{{valor.Symbol}}</td>
                    <td>R$ {{valor.LastTradePriceOnly}}</td>
                    <td ng-class="color(valor.Change)">R$ {{valor.Change}}</td>
                    <td ng-class="color(valor.ChangeinPercent)">{{valor.ChangeinPercent}}</td>
                    <td>R$ {{valor.DaysHigh}}</td>
                    <td>R$ {{valor.DaysLow}}</td>
                    <td>R$ {{valor.Open}}</td>
                    <td>R$ {{valor.PreviousClose}}</td>
                    <td>{{valor.Volume}}</td>
                </tr>
            </tbody>
        </table>
    </body>
</html>

Mapping table columns to JSOn:

  • Code = > Symbol
  • Quotation = > LastTradePriceOnly
  • Variation (RS) = > Change
  • Variation (%) = > ChangeinPercent
  • Higher quotation = > DaysHigh
  • Lower quotation = > DaysLow
  • Opening = > Open
  • Closing = > PreviousClose
  • Volume = > Volume

JSON file populating the table:

{
    "query": {
        "count": 1,
        "created": "2017-02-22T12:29:35Z",
        "lang": "pt-br",
        "results": {
            "quote": {
                "symbol": "CTSA3.SA",
                "Ask": "2.07",
                "AverageDailyVolume": "6374",
                "Bid": "1.91",
                "AskRealtime": null,
                "BidRealtime": null,
                "BookValue": "6.24",
                "Change_PercentChange": "-0.06 - -2.90%",
                "Change": "-0.06",
                "Commission": null,
                "Currency": "BRL",
                "ChangeRealtime": null,
                "AfterHoursChangeRealtime": null,
                "DividendShare": null,
                "LastTradeDate": "2/21/2017",
                "TradeDate": null,
                "EarningsShare": "-0.37",
                "ErrorIndicationreturnedforsymbolchangedinvalid": null,
                "EPSEstimateCurrentYear": null,
                "EPSEstimateNextYear": null,
                "EPSEstimateNextQuarter": "0.00",
                "DaysLow": "2.00",
                "DaysHigh": "2.07",
                "YearLow": "1.46",
                "YearHigh": "3.29",
                "HoldingsGainPercent": null,
                "AnnualizedGain": null,
                "HoldingsGain": null,
                "HoldingsGainPercentRealtime": null,
                "HoldingsGainRealtime": null,
                "MoreInfo": null,
                "OrderBookRealtime": null,
                "MarketCapitalization": "78.99M",
                "MarketCapRealtime": null,
                "EBITDA": "136000.00",
                "ChangeFromYearLow": "0.55",
                "PercentChangeFromYearLow": "+37.67%",
                "LastTradeRealtimeWithTime": null,
                "ChangePercentRealtime": null,
                "ChangeFromYearHigh": "-1.28",
                "PercebtChangeFromYearHigh": "-38.91%",
                "LastTradeWithTime": "4:29pm - <b>2.01</b>",
                "LastTradePriceOnly": "2.01",
                "HighLimit": null,
                "LowLimit": null,
                "DaysRange": "2.00 - 2.07",
                "DaysRangeRealtime": null,
                "FiftydayMovingAverage": "2.03",
                "TwoHundreddayMovingAverage": "1.84",
                "ChangeFromTwoHundreddayMovingAverage": "0.17",
                "PercentChangeFromTwoHundreddayMovingAverage": "+9.44%",
                "ChangeFromFiftydayMovingAverage": "-0.02",
                "PercentChangeFromFiftydayMovingAverage": "-0.77%",
                "Name": "SANTANENSE  ON",
                "Notes": null,
                "Open": "2.07",
                "PreviousClose": "2.07",
                "PricePaid": null,
                "ChangeinPercent": "-2.90%",
                "PriceSales": "0.23",
                "PriceBook": "0.33",
                "ExDividendDate": "5/4/2015",
                "PERatio": null,
                "DividendPayDate": null,
                "PERatioRealtime": null,
                "PEGRatio": "0.00",
                "PriceEPSEstimateCurrentYear": null,
                "PriceEPSEstimateNextYear": null,
                "Symbol": "CTSA3.SA",
                "SharesOwned": null,
                "ShortRatio": "0.00",
                "LastTradeTime": "4:29pm",
                "TickerTrend": null,
                "OneyrTargetPrice": null,
                "Volume": "6800",
                "HoldingsValue": null,
                "HoldingsValueRealtime": null,
                "YearRange": "1.46 - 3.29",
                "DaysValueChange": null,
                "DaysValueChangeRealtime": null,
                "StockExchange": "SAO",
                "DividendYield": null,
                "PercentChange": "-2.90%"
            }
        }
    }
}

Another example of JSON:

{
    "query": {
        "count": 1,
        "created": "2017-02-22T12:29:35Z",
        "lang": "pt-br",
        "results": {
            "quote": {
                "symbol": "BPHA3.SA",
                "Ask": "7.70",
                "AverageDailyVolume": "230233",
                "Bid": "7.46",
                "AskRealtime": null,
                "BidRealtime": null,
                "BookValue": "5.54",
                "Change_PercentChange": "-0.15 - -1.96%",
                "Change": "-0.15",
                "Commission": null,
                "Currency": "BRL",
                "ChangeRealtime": null,
                "AfterHoursChangeRealtime": null,
                "DividendShare": null,
                "LastTradeDate": "2/21/2017",
                "TradeDate": null,
                "EarningsShare": "-9.42",
                "ErrorIndicationreturnedforsymbolchangedinvalid": null,
                "EPSEstimateCurrentYear": "-0.50",
                "EPSEstimateNextYear": null,
                "EPSEstimateNextQuarter": "0.00",
                "DaysLow": "7.47",
                "DaysHigh": "7.90",
                "YearLow": "3.92",
                "YearHigh": "19.20",
                "HoldingsGainPercent": null,
                "AnnualizedGain": null,
                "HoldingsGain": null,
                "HoldingsGainPercentRealtime": null,
                "HoldingsGainRealtime": null,
                "MoreInfo": null,
                "OrderBookRealtime": null,
                "MarketCapitalization": "848.11M",
                "MarketCapRealtime": null,
                "EBITDA": "-296.97M",
                "ChangeFromYearLow": "3.58",
                "PercentChangeFromYearLow": "+91.33%",
                "LastTradeRealtimeWithTime": null,
                "ChangePercentRealtime": null,
                "ChangeFromYearHigh": "-11.70",
                "PercebtChangeFromYearHigh": "-60.94%",
                "LastTradeWithTime": "6:04pm - <b>7.50</b>",
                "LastTradePriceOnly": "7.50",
                "HighLimit": null,
                "LowLimit": null,
                "DaysRange": "7.47 - 7.90",
                "DaysRangeRealtime": null,
                "FiftydayMovingAverage": "6.39",
                "TwoHundreddayMovingAverage": "8.00",
                "ChangeFromTwoHundreddayMovingAverage": "-0.50",
                "PercentChangeFromTwoHundreddayMovingAverage": "-6.30%",
                "ChangeFromFiftydayMovingAverage": "1.11",
                "PercentChangeFromFiftydayMovingAverage": "+17.40%",
                "Name": "BR PHARMA   ON      NM",
                "Notes": null,
                "Open": "7.65",
                "PreviousClose": "7.65",
                "PricePaid": null,
                "ChangeinPercent": "-1.96%",
                "PriceSales": "0.41",
                "PriceBook": "1.38",
                "ExDividendDate": "5/2/2012",
                "PERatio": null,
                "DividendPayDate": null,
                "PERatioRealtime": null,
                "PEGRatio": "0.00",
                "PriceEPSEstimateCurrentYear": null,
                "PriceEPSEstimateNextYear": null,
                "Symbol": "BPHA3.SA",
                "SharesOwned": null,
                "ShortRatio": "0.00",
                "LastTradeTime": "6:04pm",
                "TickerTrend": null,
                "OneyrTargetPrice": null,
                "Volume": "268800",
                "HoldingsValue": null,
                "HoldingsValueRealtime": null,
                "YearRange": "3.92 - 19.20",
                "DaysValueChange": null,
                "DaysValueChangeRealtime": null,
                "StockExchange": "SAO",
                "DividendYield": null,
                "PercentChange": "-1.96%"
            }
        }
    }
}

I've posted the code at: link

    
asked by anonymous 22.02.2017 / 13:46

4 answers

5

Preserve the returned values in a mapped object, whose property key is the position of the paper in the papeis array. Next, change only the value of the property according to the API return.

The following changes were made:

$scope.mapa = {};

An object is created at the scope of the control to contain the returned results. Note that it is not a array , and that it is not redefined at every load cycle.

angular.forEach(papeis, function(papel, index) {[...]

The forEach() method provides an extra property, passed as the second parameter of the call - the index of the item in the source collection.

$scope.mapa[index] = resultado;

Query return to the API is stored in the map according to the index .

And in view :

<tr ng-repeat="(k, valor) in mapa">

The format (key, value) in a ngRepeat allows an object to be used as a source, the properties as value keys.

The key (which is just the index of the paper in the original array) can be ignored, but the value is used to popular your view .

(Additionally, another map object was created, $scope.prev , to help identify value differences between the current and previous requests.)

Functional example below:

angular.module("money", []);
angular.module("money")
    .controller("moneyController", function($scope, $http, $interval) {
        var papeis = ["ABEaV3.SA", "BBTG12.SA", "AGRO3.SA", "BPAN4.SA", "BPHA3.SA", "BRML3.SA", "BRSR6.SA", "BTOW3.SA", "CARD3.SA", "CIEL3.SA", "CMIG4.SA", "CTKA4.SA", "CTSA3.SA"];
        $scope.mapa = {};
        $scope.prev = {};

        function consultarCotacao() {
            angular.forEach(papeis, function(papel, index) {
                var yql = 'select * from yahoo.finance.quotes where symbol in ("' + papel + '")';
                var api = "https://query.yahooapis.com/v1/public/yql?q=" + encodeURIComponent(yql);
                var url = api + "&format=json&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys&callback=";
                $http({
                        method: "GET",
                        url: url,
                        timeout: 3000,
                        headers: { 'Content-Type': "json" }
                    })
                    .then(function successCallback(response) {
                        resultado = response.data.query.results.quote;
                        if (resultado.Name !== null) {

                            if (angular.toJson($scope.mapa[index]) !== angular.toJson(resultado)) {
                            
                                if ($scope.mapa[index]){
                                    var clone =angular.fromJson(angular.toJson($scope.mapa[index]));
                                    $scope.prev[resultado.Symbol] = clone;
                                }
                            
                                $scope.mapa[index] = resultado;
                            }
                        } else {
                            console.log(resultado.Symbol + " não foi encontrado!");
                        }
                    }, function errorCallback(response) { console.log("Falha na chamada do recurso."); });
            });
        }

        $scope.color = function(valor) {
            if (valor.indexOf("+") !== -1) {
                return "green";
            }
            return "red";
        };
        $scope.markDiff = function(a, b) {
            if (a !== b) {
                return "bold-text";
            }
            return "normal-text";
        };
        consultarCotacao();
        $interval(consultarCotacao, 5000, 0);
    });
<html lang="pt-br" ng-app="money">
    <head>
        <meta charset="UTF-8"/>
        <title>BMF&Bovespa</title>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular.js"></script><linkrel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
        <style>
            .green {
                color: green;
            }
            .red {
                color: red;
            }
            .bold-text {
                font-weight:bold;
            }
        </style>
        <script>
        </script>
    </head>
    <body ng-controller="moneyController">
        <table class="table table-striped">
            <thead>
                <tr>
                    <th>Código</th>
                    <th>Cotação</th>
                    <th>Variação (RS)</th>
                    <th>Variação (%)</th>
                    <th>Maior cotação</th>
                    <th>Menor cotação</th>
                    <th>Abertura</th>
                    <th>Fechamento</th>
                    <th>Volume</th>
                </tr>
            </thead>
            <tbody>
                <tr ng-repeat="(k, valor) in mapa">
                    <td>{{valor.Symbol}}</td>
                    <td ng-class='markDiff(valor.LastTradePriceOnly, prev[valor.Symbol].LastTradePriceOnly)'>
                    R$ {{valor.LastTradePriceOnly}}</td>
                    <td ng-class="color(valor.Change)">R$ {{valor.Change}}</td>
                    <td ng-class="color(valor.ChangeinPercent)">{{valor.ChangeinPercent}}</td>
                    <td>R$ {{valor.DaysHigh}}</td>
                    <td>R$ {{valor.DaysLow}}</td>
                    <td>R$ {{valor.Open}}</td>
                    <td>R$ {{valor.PreviousClose}}</td>
                    <td>{{valor.Volume}}</td>
                </tr>
            </tbody>
        </table>
        
        <pre>{{mapa | json}}<pre>
        <pre>{{prev | json}}<pre>
    </body>
</html>
    
17.04.2017 / 19:42
1

Use a dictionary

In order to update% internal% by just updating the items, without having to clear and regenerate array you can use a dictionary that relates the key to the index of the item and update when the result brings the same and enter it when you can not find it.

Tip based on your code in Github. I used the array, field as a key but you can put the one that best plays this role, that is, that can be identified only in the logs:

    angular.module("money", []);
    angular.module("money").controller("moneyController", function ($scope, $http, $interval) {

        var papeis = ['ABEaV3.SA','BBTG12.SA', 'AGRO3.SA', 'BPAN4.SA', 'BPHA3.SA', 'BRML3.SA', 'BRSR6.SA', 'BTOW3.SA', 'CARD3.SA', 'CIEL3.SA', 'CMIG4.SA', 'CTKA4.SA', 'CTSA3.SA'];
        var dict = {};
        $scope.valores = [];

        function consultarCotacao(papeis) {

            angular.forEach(papeis, function (papel) {

                var yql = 'select * from yahoo.finance.quotes where symbol in ("' + papel + '")';
                var api = 'https://query.yahooapis.com/v1/public/yql?q=' + encodeURIComponent(yql);
                var url = api + '&format=json&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys&callback=';

                $http({
                    method: 'GET',
                    url: url,
                    timeout: 3000,
                    headers: {'Content-Type': 'json'}
                }).then(function successCallback(response) {
                    resultado = response.data.query.results.quote;
                    if (resultado.Name !== null) {
                        var key = resultado.Symbol; 
                        if (!dict.hasOwnProperty(key)) {
                            dict[key] = $scope.valores.push(resultado) - 1;
                            console.log('insert: ' + key);
                        } else {
                            $scope.valores[dict[key]] = resultado;
                            console.log('update: ' + key);
                        }
                    } else {
                        console.log(resultado.Symbol + ' não foi encontrado!');
                    }
                }, function errorCallback(response) {
                    console.log('Falha na chamada do recurso.');
                });
            });

        }

        $scope.color = function (valor)
        {
            if (valor.indexOf("+") !== -1) {
                return "green";
            }
            return "red";
        }

        consultarCotacao(papeis);
        $interval(consultarCotacao, 5000, 0, 0, papeis);
    });
    
17.04.2017 / 14:04
0

As @Rafael B. Marcílio suggested, you can map the json to a javascript object, for example using JSON.parse:

var json = JSON.parse('[{
"symbol": "CTSA3.SA",
"Ask": "2.07",
"AverageDailyVolume": "6374"}]')

And instead of a static table, you can use ng-repeat to populate the table dynamically, as in this example:

ng-repeat documentation

<table>
<thead>
    <tr>
        <th>Código</th>
        <th>Descrição</th>
        <th>Categoria</th>
        <th>Preço Atual</th>
    </tr>
</thead>
<tbody>
    <tr ng-repeat="item in materialList">

        <td>{{item.codMaterial}}</td>
        <td>{{item.description}}</td>
        <td>{{item.category.description}}</td>
        <td>{{item.currentPrice | currency:"R$ "}}</td>
    </tr>
</tbody>

Where the MaterialList is your array that has been converted from json, so any modification in the list automatically changes the entire table

    
07.03.2017 / 14:23
0

One way that can greatly simplify you is to use angular.merge() , which makes 2 lists unified into one, updating the values of the properties in common.

That is, for every request you make, just refresh the list, so the fields update automatically, see:

$scope.valores = [];

var arr = [];

angular.forEach(papeis, function (papel) {
    $http({
        method: 'GET',
        url: url,
        timeout: 3000,
        headers: {'Content-Type': 'json'}
    }).then(function successCallback(response) {
        resultado = response.data.query.results.quote;
        if (resultado.Name !== null) {
            arr.push(resultado);
        } else {
            console.log(resultado.Symbol + ' não foi encontrado!');
        }
    }, function errorCallback(response) {
        console.log('Falha na chamada do recurso.');
    });
})

$scope.valores = angular.merge($scope.valores, arr);

Important !!

In this model, because it is very automated, you have no control of checks. For example, if by chance you get a list and after 10 minutes get another list, exactly the same (in a structure), BUT in a different order, angular.merge will update, changing the order too. That is, if the object that was first comes in 3rd on the next call, it will be the third object in the new list, etc.

If this is not a problem, there is not much to worry about.

If you want to do a deeper analysis of the object, I think it's best to make a for , analyze the objects that have been updated, and perform the update "manually". Something like this:

for ( var i=0; i < $scope.valores.length; i++ ) {
    for ( var in=0; in < arr.length; in++ ) {
        if ( $scope.valores[i].id === arr[in].id ) {
            angular.extend( $scope.valores[i], arr[in] );
        }
    }
}

Where angular.extend will copy the property values of an object to the object origin.

This ensures that you will update the correct object to which the data belongs.

I made this plunker to exemplify the scenario, already working. I just used simpler data and simulated request by clicking the button. Just click the buttons and see the changes.

    
12.04.2017 / 20:37