Chris
Chris

Reputation: 815

UI does not update when Updating ObservableArray

When a user clicks on a row from a list in the UI (html table tblAllCert), I have a click event that fires to populate another observable array that should populate a second html table (tblCertDetails). My click event fires, I can see data come back, and i can see data in the observable array, but my view does not update.

Here is an overview of the sequence in the code-

 1. user clicks on row from html table tblAllCert, which fires selectThing method.
 2. In viewmodel code, selectThing passes the row data from the row selected to the GetCertificateDetails(row) method.
 3. GetCertificateDetails(row) method calls getCertDetails(CertificateDetails, source) function on my data service (the data service gets data from a web api).  getCertDetails(CertificateDetails, source) function returns an observable array.
 4. once the data is returned to selectThing, CertificateDetails observable array is populated.  It does get data to this point. 

Step 5 should be updating the UI (specifically tblCertDetails html table), however the UI does not get updated.

Here is my view-

                                    <table id="tblAllCert" border="0" class="table table-hover" width="100%">
                                    <tbody data-bind="foreach: allCertificates">
                                        <tr id="AllCertRow" style="cursor: pointer" data-bind="click: $parent.selectThing, css: { highlight: $parent.isSelected() == $data.lwCertID }">
                                            <td>
                                                <ul style="width: 100%">

                                                    <b><span data-bind="    text: clientName"></span>&nbsp;(<span data-bind="    text: clientNumber"></span>)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span data-bind="    text: borrowBaseCount"></span>&nbsp;Loan(s)&nbsp;</b>
                                                    <br />
                                                    Collateral Analyst:&nbsp;<span data-bind="    text: userName"></span>
                                                    <br />
                                                    Certificate:&nbsp;<span data-bind="text: lwCertID"></span>&nbsp;&nbsp;Request&nbsp;Date:&nbsp;<span data-bind="    text: moment(requestDate).format('DD/MMM/YYYY')"></span>
                                            </td>
                                        </tr>
                                    </tbody>
                                </table>


                                <table id="tblCertDetails" border="0" class="table table-hover" width="100%">
                                <tbody data-bind="foreach: CertificateDetails">
                                    <tr id="Tr1" style="cursor: pointer">
                                        <td>
                                            <ul style="width: 100%">
                                                <b>Loan:&nbsp;<span data-bind="    text: loanNum"></span>
                                        </td>
                                    </tr>
                                </tbody>

                            </table>

My viewmodel-

 define(['services/logger', 'durandal/system', 'durandal/plugins/router', 'services/CertificateDataService'],
function (logger, system, router, CertificateDataService) {
    var allCertificates = ko.observableArray([]);
    var myCertificates = ko.observableArray([]);
    var isSelected = ko.observable();
    var serverSelectedOptionID = ko.observable();

    var CertificateDetails = ko.observableArray([]);

    var serverOptions = [
    { id: 1, name: 'Certificate', OptionText: 'lwCertID' },
    { id: 2, name: 'Client Name', OptionText: 'clientName' },
    { id: 3, name: 'Client Number', OptionText: 'clientNumber' },
    { id: 4, name: 'Request Date', OptionText: 'requestDate' },
    { id: 5, name: 'Collateral Analyst', OptionText: 'userName' }
    ];

    var activate = function () {
        // go get local data, if we have it
        return SelectAllCerts(), SelectMyCerts();
    };


    var vm = {
        activate: activate,
        allCertificates: allCertificates,
        myCertificates: myCertificates,
        CertificateDetails: CertificateDetails,
        title: 'Certificate Approvals',
        SelectMyCerts: SelectMyCerts,
        SelectAllCerts: SelectAllCerts,
        theOptionId: ko.observable(1),
        serverOptions: serverOptions,
        serverSelectedOptionID: serverSelectedOptionID,
        SortUpDownAllCerts: SortUpDownAllCerts,
        isSelected: isSelected,
        selectThing: function (row, event) {
            CertificateDetails = GetCertificateDetails(row);
            isSelected(row.lwCertID);
        }

    };



    serverSelectedOptionID.subscribe(function () {
        var sortCriteriaID = serverSelectedOptionID();
        allCertificates.sort(function (a, b) {
            var fieldname = serverOptions[sortCriteriaID - 1].OptionText;

            if (a[fieldname] == b[fieldname]) {
                return a[fieldname] > b[fieldname] ? 1 : a[fieldname] < b[fieldname] ? -1 : 0;
            }

            return a[fieldname] > b[fieldname] ? 1 : -1;

        });

    });

    return vm;



    function GetCertificateDetails(row) {
        var source = {
            'lwCertID': row.lwCertID,
            'certType': row.certType,
        }
        return CertificateDataService.getCertDetails(CertificateDetails, source);
    }

    function SortUpDownAllCerts() {
        allCertificates.sort();
    }


    function SelectAllCerts() {
        return CertificateDataService.getallCertificates(allCertificates);
    }

    function SelectMyCerts() {
        return CertificateDataService.getMyCertificates(myCertificates);
    }

});

And releveant excerpt from my data service-

        var getCertDetails = function (certificateDetailsObservable, source) {
        var dataObservableArray = ko.observableArray([]);
        $.ajax({
            type: "POST",
            dataType: "json",
            url: "/api/caapproval/CertDtlsByID/",
            data: source,
            async: false,
            success: function (dataIn) {

                ko.mapping.fromJSON(dataIn, {}, dataObservableArray);

            },
            error: function (error) {
                jsonValue = jQuery.parseJSON(error.responseText);
                //jError('An error has occurred while saving the new part source: ' + jsonValue, { TimeShown: 3000 });
            }

        });
        return dataObservableArray;
    }

Ideas of why the UI is not updating?

Upvotes: 0

Views: 455

Answers (1)

ejegg
ejegg

Reputation: 358

When you're using Knockout arrays, you should be changing the contents of the array rather than the reference to the array itself. In selectThing, when you set

CertificateDetails = GetCertificateDetails(row);

the new observableArray that's been assigned to CertificateDetails isn't the same observableArray that KO bound to your table.

You're very close to a solution, though. In your AJAX success callback, instead of creating a new dataObservableArray and mapping into that, you can perform the mapping directly into parameter certificateDetailsObservable. ko.mapping.fromJSON should replace the contents of the table-bound array (you would also get rid of the assignment in selectThing).

Upvotes: 1

Related Questions