0

Inside a (dynamically generated) table I search for and find the column named "VATNumber" and iterate over all (non-header) cells of that column, using the value (vat number) as a parameter to an ajax call.

The table and button:

<table id="ResultData">
    <tbody>
        <tr>
            <th>ID</th><th>Amount</th><th>VATNumber</th><th>Country</th>
        </tr><tr>
            <td>1</td><td>10000.00</td><td>DE123456789</td><td>BE</td>
        </tr><tr>
            <td>2</td><td>20000.00</td><td>NL000123456B01</td><td>BE</td>
        </tr><tr>
            <td>3</td><td>30000.00</td><td>NL001234567B01</td><td>BE</td>
        </tr><tr>
            <td>4</td><td>40000.00</td><td>NL002345678B01</td><td>BE</td>
        </tr>
    </tbody>
</table>

<p>
    <button id="btnVIESCheck">CHECK</button>
</p>

This is more or less what the code looks like with (icky) synchronous ajax (contradiction in terms)

$('#btnVIESCheck').on('click', function (e) {
    e.preventDefault();
    var columnIndex = $('#ResultData th:contains("VATNumber")').index();
    $('#ResultData tr:gt(0) td:nth-child(' + (columnIndex + 1) + ')')
      .each(function () {
        $this = $(this);
        var vatnr = $this.text(),
            rowIndex = $this.closest('tr').index();

        //add ajaxloader
        $this.html($this.html() + '&nbsp;' + ajaxloader);

        var resultCode = checkVATNr(vatnr),
            img = '';

        switch (resultCode) {
            case '1':
                img = '<img src="Img/ok.jpg" />';
                break;
            case '2':
                img = '<img src="Img/notok.jpg" />';
                break;
            case '3':
                img = '<img src="Img/warning.jpg" />';
                break;
            default:
                break;
        }

        $this.html(vatnr + ' ' + img);
    });
});

function checkVATNr(vatnr) {
    var result = '';

    $.ajax({
        type: "POST",
        async: false,
        url: 'MyWebService.asmx/CheckVATNumber',
        contentType: "application/json; charset=utf-8",
        dataType: 'json',
        data: JSON.stringify({
            'vatnr': vatnr
        }),
        success: function (msg) {
            var xml = $.parseXML(msg.d);
            $xml = $(xml);
            result = $xml.find('ResultCode').text();
        },
        error: function (e) {
            console.log("Error " + e.error);
        }
    });

    return result;
}

Of course

  • this shows the images when ALL the calls are done. Not preferable, the table might be very large
  • I'd like to use proper async ajax inside the .each(), but then sometimes the results are garbled, out of order, replacing the wrong table cells and sometimes it only works for the last cell.

How can I write this using promises?

Wim Ombelets
  • 5,097
  • 3
  • 39
  • 55

1 Answers1

2

One problem is the use of global variable $this in the each loop, which will result in $this being always referring to the last td element. So use define it as a local variable like var $this = $(this);

That is because of the use of async:false, which blocks the browser thread from doing anything till all the ajax requests are finished.

Use callback mechanism to process the requests like

$('#btnVIESCheck').on('click', function (e) {
    e.preventDefault();
    var columnIndex = $('#ResultData th:contains("VATNumber")').index();
    $('#ResultData tr:gt(0) td:nth-child(' + (columnIndex + 1) + ')')
        .each(function () {
        var $this = $(this);
        var vatnr = $this.text(),
            rowIndex = $this.closest('tr').index();

        //add ajaxloader
        $this.html($this.html() + '&nbsp;' + ajaxloader);

        checkVATNr(vatnr, function (resultCode) {
            var img = '';
            switch (resultCode) {
                case '1':
                    img = '<img src="Img/ok.jpg" />';
                    break;
                case '2':
                    img = '<img src="Img/notok.jpg" />';
                    break;
                case '3':
                    img = '<img src="Img/warning.jpg" />';
                    break;
                default:
                    break;
            }

            $this.html(vatnr + ' ' + img);
        })

    });
});

function checkVATNr(vatnr, callback) {
    $.ajax({
        type: "POST",
        url: 'MyWebService.asmx/CheckVATNumber',
        contentType: "application/json; charset=utf-8",
        dataType: 'json',
        data: JSON.stringify({
            'vatnr': vatnr
        }),
        success: function (msg) {
            var xml = $.parseXML(msg.d);
            $xml = $(xml);
            callback($xml.find('ResultCode').text());
        },
        error: function (e) {
            console.log("Error " + e.error);
            callback('');
        }
    });
}

Also see: How do I return the response from an asynchronous call?

Community
  • 1
  • 1
Arun P Johny
  • 384,651
  • 66
  • 527
  • 531