Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Knockout refresh observableArray in the html

I'm working on Android with Jquery Mobile with PhoneGap using Knockout for loading data.

I'm getting the data all right and loading it on the HTML page accordingly to the for each data-bind I have on the tags.

When I want to refresh the data, it just doesn't do it. It returns just an HTML without bound data or throwing dom exception not found.

My applyBinding happens inside pagecreate event of the page.

I've posted a simple example describing the problem on my SkyDrive - http://sdrv.ms/LpUdLt It's a public example reproducing the problem. the viewmodel holds an array that holds array. refreshed with randomal values. trying to reload the page in jquery mobile with changepage reload with new data by pressing the navbar button fails with the dom object mistake. I do agree not to that I shouldn't create an instance of VM every page create, just can't find a way to implement it, so that the data will be rerendered on HTML.

   //indexPage.js
   var wAViewModelInst ;
   var viewPageIndexContent;
   $('#pageIndex').live('pagecreate', function (event) { 
        viewPageIndexContent = document.getElementById("pageIndexContent");
        wAViewModelInst = new WAViewModel(true);
        ko.applyBindings(wAViewModelInst, viewPageIndexContent);

        waHeaderVM.refreshContentData = function () {

              // wAViewModelInst.updateRowList();
              // ko.cleanNode(viewPageIndexContent);
              // viewPageIndexContent = document.getElementById("pageIndexContent");
              //wAViewModelInst = new WAViewModel(true);
             //ko.applyBindings(wAViewModelInst, viewPageIndexContent);
              $.mobile.changePage("index.html", { allowSamePageTransition: true, reloadPage: true });
              $.mobile.hidePageLoadingMsg();
       }
   }

    //WAViewModel
    self.WARowList = ko.observableArray();
    self.updateRowList = function () {
        self.WARowList(self.GetWA());
    }
    //tried the exteding 
    //ko.observableArray.fn.WARowListUpdate = function () {
    //    //create a sub-observable
    //    this.hasItems = ko.observable();

    //    //update it when the observableArray is updated
    //    this.subscribe(function (newValue) {
    //        this.hasItems(newValue && newValue.length ? true : false);
    //    }, this);

    //    //trigger change to initialize the value
    //    this.valueHasMutated();

    //    //support chaining by returning the array
    //    return this;
    //};            

is there any way to update the html after the first rendering ?

adding the html code:

<div id="pageIndex" data-role="page" data-transition="flip" data-theme="e" data-dom-cache="true">
    <div id="indexHeader" data-role="header" data-theme="e">
        <div data-role="navbar" data-iconpos="right">
            <ul>
                <li><a href="login.html" data-role="tab" data-icon="back" data-bind="click: loadingHandler"
                    class="brighter-text">חזרה</a></li>
                <li><a href="#" data-role="tab" data-icon="refresh" data-bind ="click: refreshContentData" >רענן</a></li>
                <li><a href="researchEvent.html" data-role="tab" data-icon="check" id="navBarResearchEventPage"   data-bind="click: loadResearchEvent" class="brighter-text">ביצוע חקר</a></li>
                <li><a href="#" data-role="tab" data-icon="grid" class="ui-btn-active brighter-text">
                    סידור עבודה</a></li>
            </ul>
        </div>
    </div>
    <div id="pageIndexContent" data-role="content" data-theme="e" style="padding-bottom: 52px;
        height: 570px;">
        <h2 data-bind="text:Title" class="brighter-text" style="font-size:22pt;">
        </h2>
        <div data-bind="foreach: WARowList" style="width: 99%; text-align: center">
            <div>
                <table style="float: right; width: 20%; height: 60px;" cellpadding="0" cellspacing="0">
                    <tr data-bind="visible : FirstRow " style="height: 31px;">
                        <th class="AlignedHeader">
                            <label>
                                שעה / שילוט</label>
                        </th>
                    </tr>
                    <tr data-bind="style: { backgroundColor: Odd() ? '#8CC63F' : '#AFC493' }">
                        <td style="width: 20%;" data-bind="style: { backgroundColor: IsNew() ? 'yellow' : 'transparent' }">
                            <input  type="button" data-bind="click: ShowSampleDetails, value: ShilutTime , jQueryButtonUIEnableDisable:$data"
                                data-icon="plus" data-iconpos="right"/>
                        </td>
                    </tr>
                </table>
                <table style="height: 60px; width: 80%; background-color: #8CC63F;" data-bind="style: { backgroundColor: Odd() ? '#8CC63F' : '#AFC493' }"
                    cellpadding="0" cellspacing="0">
                    <thead data-bind="if : FirstRow">
                        <tr data-bind="foreach: CellList">
                            <th class="AlignedHeader">
                                <label data-bind="text: Date">
                                </label>
                            </th>
                        </tr>
                    </thead>
                    <tbody>
                        <tr data-bind="foreach: CellList">
                            <td style="width: 11.5%;">
                                <div data-bind="visible:IsPopulated ">
                                    <div data-bind="visible: HasDrivers">
                                        <input type="button" data-role="button" data-bind="click: ShowBusDriverList.bind($data , $root) , jQueryButtonUIEnableDisable: $data "
                                            data-icon="search" data-iconpos="right" value="נהגים" class="brighter-text" />
                                    </div>
                                    <div data-bind="visible: !HasDrivers()">
                                        <input type="button" data-role="button" id="btnNoDriver" disabled="disabled" data-icon="info"
                                            data-iconpos="right" value="אין נהג" class="brighter-text" />
                                    </div>
                                </div>
                                <div data-bind="visible: !IsPopulated">
                                </div>
                            </td>
                        </tr>
                    </tbody>
                </table>
            </div>

and so on ..

the GetWA function returns an observableArray of warow list . it works the first time the trouble is rerendering the dom object. the dom element is contaminated with ko and fails ..

I tried the answer of Luffy :

var lVM = new loginViewModel();
var  footerViewModelLogin = {
        IsOnline: ko.observable(globalContext.Network()),
        IsSync: ko.observable(globalContext.Sync())
    };
$('#login').live('pagecreate', function (event) {


     viewLoginContent = document.getElementById("loginContent");

    ko.applyBindingsToNode(viewLoginContent, lVM);


    viewLoginFooter= document.getElementById("footerLogin");

    ko.applyBindingsToNode(viewLoginFooter, footerViewModelLogin);


});


$('#login').live('pagehide', function (event, ui) {
    $.mobile.hidePageLoadingMsg();

});



function loginViewModel() {
    var self = this;

    try {

        self.userName = ko.observable("");
        self.password = ko.observable("");
        self.message = ko.observable("");

        self.CleanGlobalContext = function () {
          ...

        };

        self.Validate = function () {

            ...

        };
    }
    catch (e) {
        if (IsDebug) alert("GlobalContext.prototype.SetMapOverlay  " + e.message);
        if (typeof (console) != 'undefined' && console) console.log("GlobalContext.prototype.SetMapOverlay "     + e.message);
    }
}

ko.applyBindings(lVM);
ko.applyBindings(footerViewModelLogin);

The knockout fails without the element predefined event to bind .

like image 749
Tzvi Gregory Kaidanov Avatar asked Jun 25 '12 07:06

Tzvi Gregory Kaidanov


People also ask

What is Ko observableArray?

Behind the scenes, an observableArray is actually an observable whose value is an array (plus, observableArray adds some additional features described below). So, you can get the underlying JavaScript array by invoking the observableArray as a function with no parameters, just like any other observable.

How do you clear a knockout observable array?

To clear an observableArray you can set the value equal to an empty array.

What is Knockout used for?

Knockout is a JavaScript library that helps you to create rich, responsive display and editor user interfaces with a clean underlying data model.


1 Answers

I have not understood well you difficulty. However, I don't understand why you need to reapply bindings. Notice what follows:

  1. If you change an observable array the needed templates are re-instantiated to updated coherently the UI. However you have to update the observalble array either by using the observable array functions, or by passing to the observable a new array: obs(newArray).
  2. If you need to perform actions after the ko re-rendering you just need to use the afterRender, or afterAdd parameters of the template binding
  3. if you add new jquery mobile code you have to parse it after having created it otherwise jquery mobile will not work...again you can do this in the afterRender function.
  4. it is problematic to load html containing ajax ko bindings via ajax. The right approach is to create that content as a template, that you render immediately (not with ajax). Then you query the server just to get new json content. Then you can instantiate the template by using for instance the with binding.
like image 131
Francesco Abbruzzese Avatar answered Oct 13 '22 22:10

Francesco Abbruzzese