Search code examples
jsrenderjsviews

JSViews observable array not refreshing


Apologies for another question on JSViews. I really love this system, but I am struggling occasionally.

I have a simple template which only needs to update which items in the array to show on select box change:

However, when I change the data "isSelected" the template is not being updated with the following:

ie:

function someClickFunction(sel) {
    value = sel.value;
    ....
    la_configure_list[0].isSelected = false;
    $.observable(la_configure_list).refresh(
            la_configure_list.slice(0)
        );
}

if I change it to

$.observable(la_configure_list).refresh(
        JSON.parse(JSON.stringify(la_configure_list))
                       );

it updates just fine.

Can anyone tell me what I am doing wrong:

<script id="la-config-overview-list-template" type="text/x-jsrender">
    {^{if isSelected}}
    <div class="col s12 la_review_list_item" data-index="{{:#index}}">
        <div class="card">
            <div class="card-content" style="padding: 10px;">
                <div class="col s12 m6 l5 xl5">
                    <h6 style="font-weight: 700;" class="">{{:title}}</h6>
                        <div>Categories:</div>
                        <div>
                            {{for categories}}
                            <span class="chip">{{:#data}}</span>
                            {{/for}}
                        </div>
                    </div>
                    <div class="clearfix"></div>
                </div>
            </div>
        </div>
    </div>
    {{/if}}

my html is

<div id="teacher_la_review_overview_main_wrapper" class="container body-content">
    <div class="row" style="margin-bottom: 0;"></div>
</div>

the array is:

la_configure_list = [
{
    isSelected:true,
    title: "Title",
    categories: ["1", "2"....]
},
....
]

I set up the template:

$.templates("#la-config-overview-list-template").link("#la_review_list_overview_list_wrapper .row", la_configure_list);

I tried using $.view(elem).refresh(), but as my click action to update isSelected is outside the template, I cannot find the correct "elem" to use.

EDIT - Added actual update code:

function CategoryUpdate(sel) {

        var catSelected = sel.value;
        var catText = sel.options[sel.selectedIndex].text;

        if (catSelected !== "-1") {
            for (var cs = 0; cs < la_configure_list.length; cs++) {
                if (la_configure_list[cs].categories == null) {
                    la_configure_list[cs].isSelected = false;
                    continue;
                }
                var categories = la_configure_list[cs].categories;
                var isFound = false;
                for (var c = 0; c < categories.length; c++) {
                    var category = categories[c];
                    if (category === catText) {

                        la_configure_list[cs].isSelected = true;
                        isFound = true;
                    }
                }
                if (!isFound) {
                    la_configure_list[cs].isSelected = false;
                }
            }
        } else {
            for (var x = 0; x < la_configure_list.length; x++) {
                la_configure_list[x].isSelected = true;
            }
        }

        $.observable(la_configure_list).refresh(
            JSON.parse(JSON.stringify(la_configure_list))
        );
    }

Solution

  • Your array la_configure_list.slice(0) is a shallow copy of la_configure_list. The observable array refresh() method is used for array updates – sorting, removing or adding items. It is optimized to avoid unnecessary updates. In fact it analyses the array and triggers move, insert and remove events for any changes it detects.

    In your case although you copied the array, it is a shallow copy, so the array items are the same, and the order is unchanged. Therefore no observable array change event is triggered. When you use JSON.parse etc you are replacing the object by cloned copies, so now refresh() considers that all the items have been removed and then new ones added.

    But why don't you make any change to the isSelected properties observable, along the lines of:

    function someClickFunction(sel) {
        value = sel.value;
        ...
        $.observable(la_configure_list[index]).propertyChange("isSelected", false);
        ...
    }
    

    Since you have observable data-linking {^{if isSelected}} you should not need to do any additional refreshing of either the array or the views…