0

I know this question has been asking many times, but I still can't find any solution ! Here is my code : I'm programming an array of cryptocurrency's values but I'm stuck when I'm trying to create graphs.

ajaxGet("https://api.coinmarketcap.com/v1/ticker/?limit=300", function (reponse) {
    // Transforme la réponse en tableau d'objets JavaScript
    var monnaie = JSON.parse(reponse);
    $("#tableBody").empty();      

    var tr;


    function page1(event) { 

        var timestamp = Math.round(new Date().getTime() / 1000);
        console.log(timestamp);

        for (var i = 0; i < 50; i++) {


            ajaxGet("https://min-api.cryptocompare.com/data/histohour?fsym=" + monnaie[i].symbol + "&tsym=USD&limit=28&aggregate=6&toTs=" + timestamp, function (reponse2) {

                var monnaieGraph = JSON.parse(reponse2)['Data'];
                var points = '';
                var maximum = Math.max.apply(Math,monnaieGraph.map(function(o){return o.high;}))
                var minimum = Math.min.apply(Math,monnaieGraph.map(function(o){return o.low;}))


                for (var j=0; j<=28; j++){
                    var moyenne = ((monnaieGraph[j].high)+(monnaieGraph[j].low))/2;
                    echelle = ((-100/(maximum-minimum))*(moyenne-minimum))+100;

                    points += (j*12) + ',' + echelle + '\n';
                }
            });


            tr = $('<tr/>');
            tr.append("<td>" + monnaie[i].rank + "</td>");
            tr.append("<td>" + "<img class=imageCrypto src=logos/" + monnaie[i].symbol + ".png" + " " + "style=width:37.5px; height:37.5px;" + ">" + "<div class=nomCurr><a href=http://www.google.com/" + monnaie[i].id +">" + monnaie[i].name + "</a>" + "<div>" + monnaie[i].symbol + "</div></div></td>");
            tr.append("<td>" + formatter.format(monnaie[i].price_btc) + " BTC" + "</td>");
            tr.append("<td>" + "$ " + formatter.format(monnaie[i].price_usd) + "</td>");
            tr.append("<td>" + "$ " + formatter.format(monnaie[i].market_cap_usd) + "</td>");
            tr.append("<td>" + formatter.format(monnaie[i].available_supply) + " " + monnaie[i].symbol + "</td>");
            if (Number(monnaie[i].percent_change_24h) > 0) {
                tr.append("<td class=pourcentPositif>" + Number(monnaie[i].percent_change_24h) + "%" + "<i class='fa fa-caret-up'" + "</td>");
                //tr.append("<i class=fa fa-caret-up>" + "</i>");
            }
            else {
                tr.append("<td class=pourcentNegatif>" + Number(monnaie[i].percent_change_24h) + "%" + "<i class='fa fa-caret-down'" + "</td>");
            }
            tr.append('<td><svg viewBox="0 0 336 100" width=116 height=35><polyline fill=none stroke=#0074d9 stroke-width=6 points="' + points + '"/></svg></div>');
            $('#tableBody').append(tr);

        }
    }

Everything works ! I got an array with all the values I want.

And as I said, I'm trying to add a column of graphs for each currency ! I tried 3 different code but none of them are working ! Here are the 3 codes with an image of each result:

for (var i = 0; i < 50; i++) {


            ajaxGet("https://min-api.cryptocompare.com/data/histohour?fsym=" + monnaie[i].symbol + "&tsym=USD&limit=28&aggregate=6&toTs=" + timestamp, function (reponse2) {

                var monnaieGraph = JSON.parse(reponse2)['Data'];
                var points = '';
                var maximum = Math.max.apply(Math,monnaieGraph.map(function(o){return o.high;}))
                var minimum = Math.min.apply(Math,monnaieGraph.map(function(o){return o.low;}))


                for (var j=0; j<=28; j++){
                    var moyenne = ((monnaieGraph[j].high)+(monnaieGraph[j].low))/2;
                    echelle = ((-100/(maximum-minimum))*(moyenne-minimum))+100;

                    points += (j*12) + ',' + echelle + '\n';
                }
            });


            tr = $('<tr/>');
            tr.append("<td>" + monnaie[i].rank + "</td>");
            tr.append("<td>" + "<img class=imageCrypto src=logos/" + monnaie[i].symbol + ".png" + " " + "style=width:37.5px; height:37.5px;" + ">" + "<div class=nomCurr><a href=http://www.google.com/" + monnaie[i].id +">" + monnaie[i].name + "</a>" + "<div>" + monnaie[i].symbol + "</div></div></td>");
            tr.append("<td>" + formatter.format(monnaie[i].price_btc) + " BTC" + "</td>");
            tr.append("<td>" + "$ " + formatter.format(monnaie[i].price_usd) + "</td>");
            tr.append("<td>" + "$ " + formatter.format(monnaie[i].market_cap_usd) + "</td>");
            tr.append("<td>" + formatter.format(monnaie[i].available_supply) + " " + monnaie[i].symbol + "</td>");
            if (Number(monnaie[i].percent_change_24h) > 0) {
                tr.append("<td class=pourcentPositif>" + Number(monnaie[i].percent_change_24h) + "%" + "<i class='fa fa-caret-up'" + "</td>");
                //tr.append("<i class=fa fa-caret-up>" + "</i>");
            }
            else {
                tr.append("<td class=pourcentNegatif>" + Number(monnaie[i].percent_change_24h) + "%" + "<i class='fa fa-caret-down'" + "</td>");
            }

            tr.append('<td><svg viewBox="0 0 336 100" width=116 height=35><polyline fill=none stroke=#0074d9 stroke-width=6 points="' + points + '"/></svg></div>');

            $('#tableBody').append(tr);

        }

Inside my "for" loop, I added the ajaxGet, in which I create a variable "points" which get the values from the API scaled for the graph.

This code doesn't work, I got the error: "points is not defined".

Here is my 2nd try :

for (var i = 0; i < 50; i++) {

            tr = $('<tr/>');
            tr.append("<td>" + monnaie[i].rank + "</td>");
            tr.append("<td>" + "<img class=imageCrypto src=logos/" + monnaie[i].symbol + ".png" + " " + "style=width:37.5px; height:37.5px;" + ">" + "<div class=nomCurr><a href=http://www.google.com/" + monnaie[i].id +">" + monnaie[i].name + "</a>" + "<div>" + monnaie[i].symbol + "</div></div></td>");
            tr.append("<td>" + formatter.format(monnaie[i].price_btc) + " BTC" + "</td>");
            tr.append("<td>" + "$ " + formatter.format(monnaie[i].price_usd) + "</td>");
            tr.append("<td>" + "$ " + formatter.format(monnaie[i].market_cap_usd) + "</td>");
            tr.append("<td>" + formatter.format(monnaie[i].available_supply) + " " + monnaie[i].symbol + "</td>");
            if (Number(monnaie[i].percent_change_24h) > 0) {
                tr.append("<td class=pourcentPositif>" + Number(monnaie[i].percent_change_24h) + "%" + "<i class='fa fa-caret-up'" + "</td>");
                //tr.append("<i class=fa fa-caret-up>" + "</i>");
            }
            else {
                tr.append("<td class=pourcentNegatif>" + Number(monnaie[i].percent_change_24h) + "%" + "<i class='fa fa-caret-down'" + "</td>");
            }

            ajaxGet("https://min-api.cryptocompare.com/data/histohour?fsym=" + monnaie[i].symbol + "&tsym=USD&limit=28&aggregate=6&toTs=" + timestamp, function (reponse2) {

                var monnaieGraph = JSON.parse(reponse2)['Data'];
                var points = '';
                var maximum = Math.max.apply(Math,monnaieGraph.map(function(o){return o.high;}))
                var minimum = Math.min.apply(Math,monnaieGraph.map(function(o){return o.low;}))


                for (var j=0; j<=28; j++){
                    var moyenne = ((monnaieGraph[j].high)+(monnaieGraph[j].low))/2;
                    echelle = ((-100/(maximum-minimum))*(moyenne-minimum))+100;

                    points += (j*12) + ',' + echelle + '\n';
                }

                tr.append('<td><svg viewBox="0 0 336 100" width=116 height=35><polyline fill=none stroke=#0074d9 stroke-width=6 points="' + points + '"/></svg></div>');
            });

            $('#tableBody').append(tr);

        }

This time, I added the creation of the column inside the ajaxGet. The graphs are ok, but they only appear as a row, on the last row !

And here is my last try:

for (var i = 0; i < 50; i++) {


            ajaxGet("https://min-api.cryptocompare.com/data/histohour?fsym=" + monnaie[i].symbol + "&tsym=USD&limit=28&aggregate=6&toTs=" + timestamp, function (reponse2) {

                var monnaieGraph = JSON.parse(reponse2)['Data'];
                var points = '';
                var maximum = Math.max.apply(Math,monnaieGraph.map(function(o){return o.high;}))
                var minimum = Math.min.apply(Math,monnaieGraph.map(function(o){return o.low;}))


                for (var j=0; j<=28; j++){
                    var moyenne = ((monnaieGraph[j].high)+(monnaieGraph[j].low))/2;
                    echelle = ((-100/(maximum-minimum))*(moyenne-minimum))+100;

                    points += (j*12) + ',' + echelle + '\n';
                }

            tr = $('<tr/>');
            tr.append("<td>" + monnaie[i].rank + "</td>");
            tr.append("<td>" + "<img class=imageCrypto src=logos/" + monnaie[i].symbol + ".png" + " " + "style=width:37.5px; height:37.5px;" + ">" + "<div class=nomCurr><a href=http://www.google.com/" + monnaie[i].id +">" + monnaie[i].name + "</a>" + "<div>" + monnaie[i].symbol + "</div></div></td>");
            tr.append("<td>" + formatter.format(monnaie[i].price_btc) + " BTC" + "</td>");
            tr.append("<td>" + "$ " + formatter.format(monnaie[i].price_usd) + "</td>");
            tr.append("<td>" + "$ " + formatter.format(monnaie[i].market_cap_usd) + "</td>");
            tr.append("<td>" + formatter.format(monnaie[i].available_supply) + " " + monnaie[i].symbol + "</td>");
            if (Number(monnaie[i].percent_change_24h) > 0) {
                tr.append("<td class=pourcentPositif>" + Number(monnaie[i].percent_change_24h) + "%" + "<i class='fa fa-caret-up'" + "</td>");
                //tr.append("<i class=fa fa-caret-up>" + "</i>");
            }
            else {
                tr.append("<td class=pourcentNegatif>" + Number(monnaie[i].percent_change_24h) + "%" + "<i class='fa fa-caret-down'" + "</td>");
            }

            tr.append('<td><svg viewBox="0 0 336 100" width=116 height=35><polyline fill=none stroke=#0074d9 stroke-width=6 points="' + points + '"/></svg></div>');

            $('#tableBody').append(tr);
            });
        }

I added the declaration of the whole table, inside the ajaxGet ! I don't understand the result : This time, the graphs are what I want, but the rest of the array (every columns in every rows) have the value of the 51th coin (but the "graphs" column works), while my variable i is supposed to be 0

Hope my problem is clear, I'm sorry I can't post images... Thank you for your time !

Hara
  • 57
  • 7

1 Answers1

1

I think the first of the three code snippets should work, if you change the following things:

The scope of your points variable is limited to the callback function you provide to the ajaxGet function. Therefore when you try to use it when building your table, it can't be accessed. That is why you get the error points is not defined. Declare the points variable in scope of your for-loop to solve this.

When building your points you delimit your point pairs by \n, I'm not sure if that works in your SVG code in this case. Maybe try to delimit by a blank space.

The problem with the SVG images not showing is because apparently you have to reload the actual HTML that you generated to get the SVG images rendered in the browser. See this related anwer. In your case you should reload the HTML of your table every time after a row is added. Using jQuery it would look something like this:

// Row creation logic...

$('#tableBody').append(tr);
$("#tableBody").html($("#tableBody").html());

Edit:

  • Solved scoping and async issues by using closures.
  • Used separate functions for somewhat improved readability.
  • Used jQuery to build the table and SVG elements.

function getTimestamp() {
    return Math.round(new Date().getTime() / 1000);
}

function getAveragePrice(pricepoint) {
    return (pricepoint.high + pricepoint.low) / 2;
}

function getPointMap(min, max) {
    return function (price, index) {
        var range = max - min;
        var offset = price - min;
        var value = 100 - (offset * 100 / range);

        return `${index * 12},${value}`;
    }
}

function getPoints(prices) {
    var min = Math.min(...prices);
    var max = Math.max(...prices);
    
    var points = prices.map(getPointMap(min, max));

    return points.join(" ");
}

function getSvg(points) {
    var line = $('<polyline/>');
    line.attr("fill", "none");
    line.attr("stroke", "#0074d9");
    line.attr("stroke-width", 6);
    line.attr("points", points);

    var svg = $('<svg/>');
    svg.attr("viewBox", "0 0 336 100");
    svg.attr("width", 116);
    svg.attr("height", 35);
    svg.append(line);

    return svg;
}

function getCell(content) {
    return $("<td/>").append(content);
}

function getRow(coin, points) {
    // Set id to SortOrder provided by API so 
    // we can use it to add the rows to the 
    // table in the right order.
    var tr = $('<tr/>');
    tr.attr("id", coin.SortOrder);

    tr.append(getCell(coin.SortOrder));
    tr.append(getCell(coin.CoinName));
    tr.append(getCell(`${coin.TotalCoinSupply}`));
    
    tr.append(getCell(getSvg(points)));

    return tr;
}

function addRow(row, table)
{
    var current = table.children().first();

    // While there is a next row
    while(current.is("tr")) {
        // Check if the rank of current row is 
        // larger than the rank of the row we 
        // want to add.
        if(Number(current.attr("id")) > Number(row.attr("id"))) {
            // If true, add the row before the 
            // current row and we are done.
            current.before(row);
            return;
        }

        // Else check the next row.
        current = current.next();
    }

    // If the table does not contain rows yet or 
    // the end of the table is reached without 
    // finding larger ranks, just add the row at 
    // the end.
    table.append(row);
}

function getHistoryCallback(table, coin) {
    return function (history) {
        var prices = history.Data.map(getAveragePrice);
        var points = getPoints(prices);
        var row = getRow(coin, points);
        
        addRow(row, table);
        table.html(table.html());
    }
}

function sortCoins(a, b) {
    return a.SortOrder - b.SortOrder;
}

function getCoinsCallback(table) {
    return function (coinlist) {
        var coins = Object.values(coinlist.Data).sort(sortCoins);
        
        table.empty();

        for (var i = 0; i < 10; i++) {
            var coin = coins[i];

            var url = "https://min-api.cryptocompare.com/data/histohour";
            var data = {
                fsym: coin.Symbol,
                tsym: "USD",
                limit: 28,
                aggregate: 6,
                toTs: getTimestamp()
            };
            var callback = getHistoryCallback(table, coin);

            $.getJSON(url, data, callback);
        }
    }
}

function populateTable(table_id) {
    var table = $(`#${table_id}`);
    
    var url = "https://min-api.cryptocompare.com/data/all/coinlist";
    var callback = getCoinsCallback(table);

    $.getJSON(url, callback);
}

$(document).ready(function () {
    populateTable("tableBody");
});
Community
  • 1
  • 1
jvdh
  • 309
  • 2
  • 11
  • As you said, I declared `points` in the for-loop ! The column is well created, but the graphs are empty : the polyline exists but there are no points inside. – Hara Feb 08 '18 at 20:43
  • I'm not sure if it is the new line delimiting, since it is allowed by the SVG spec. There is also a trailing `` at the end of your SVG creation line. Could you provide a minimal code snippet just creating that SVG part? – jvdh Feb 08 '18 at 23:11
  • I removed `/n` and tried to replace it with a blank space, but same result. I don't know if the problem can come from here, the variable works in a try I showed u in my first post. And I removed the ``, it was a mistake, it didn't have anything to do here ! – Hara Feb 09 '18 at 00:43
  • I think it has to do with the fact that your Ajax calls are asynchronous, so your program continues after the Ajax request and creates the SVG images before the points are created. – jvdh Feb 09 '18 at 16:25
  • Thank you ! I've never used this style of code (the callbacks and this style of ajax request). It's interesting to learn it ! I still have some issues to understand everything, but I'm able to adapt it with what I really want ! I just have 2 problems I don't understand : - the data doesn't appear in the table orderly - I'd like to change the 2nd API I use `https://api.coinmarketcap.com/v1/ticker/` into `https://min-api.cryptocompare.com/data/all/coinlist` to get the name of each currency but I don't really understand how your requests work :/ Thank you for your time ! – Hara Feb 10 '18 at 19:56
  • `$.getJSON()` is just a jQuery wrapper around a normal Ajax call. I used it as a shorthand to save me some typing ;) It excepts a base URL, a dictionary with name/value pairs (which will be added to the URL for you) and a callback function. When the Ajax call succeeds it decodes the returned JSON for you before passing it to the callback function. With regard to your two questions: 1. Ajax calls are asynchronous by nature, therefore the data will not be returned in order. 2. You can use the other API by just using the other URL and passing the right name/value pairs. I'll update the answer. – jvdh Feb 12 '18 at 00:13