Search code examples
javascriptjqueryjsviews

jsViews - re-evaluate 'if' tag on root property change


I have created a table/grid using jsViews. Each row has an edit button which when clicked selects the row and shows input controls instead of text values.

If I show/hide the inputs using data-link="visible{:#parent.parent.data.selectedIndex!==#index}" then it works fine.

However, I was trying a different approach using {^{if #parent.parent.data.selectedIndex===#index}}...{{else}}...{{/if}} to show/hide the inputs and this doesn't work when selectedIndex changes on my data object.

I also tried with {^{if ~root.selectedIndex===#index}} but that didn't work either. Is it possible to do this with {{if}}? The reason I am trying this over the first method that worked was to avoid rendering lots of select boxes which will just be hidden anyway.

My data object looks like this:

app = {
    data: [...],

    selectedIndex: null,

    select: function select(index) {
        if (this.selectedIndex !== index) {
            $.observable(this).setProperty("selectedIndex", index);
        }
    }
};

I link the template like this:

$.templates("#myTemplate").link("#divHolder", app)
    .on("click", ".data .editButton", function() {
        app.select($.view(this).index);
    })
    .on("click", ".data .saveButton", function() {
        // save details
    })
    .on("click", ".transmittals .cancelButton", function() {
        // reset values

        app.select(null);
    });

My template is like this:

<script id="myTemplate" type="text/x-jsrender">
    <table id="tblData" class="data">
        <thead>
            <tr>
                <th></th>
                <th>A</th>
                <th>B</th>
                <th>C</th>                    
            </tr>
        </thead>
        <tbody>
            {^{for data}}
            <tr class="item">
                <td>
                    {{if #parent.parent.data.selectedIndex===#index}}
            <span class="editItem">
                <button class="cancelButton">Cancel</button></span>
                    {{else}}
            <span class="viewItem">
                <button class="editButton">Edit</button></span>
                    {{/if}}                
                </td>
                <td>
                    {^{if #parent.parent.data.selectedIndex===#index}}
            <span class="editItem"><input type="text" data-link="B" /></span>
                    {{else}}
            <span class="viewItem" data-link="B"></span>
                    {{/if}}
                </td>
                <td>
                    {^{if #parent.parent.data.selectedIndex===#index}}
            <span class="editItem"><input type="text" data-link="C" /></span>
                    {{else}}
            <span class="viewItem" data-link="C"></span>
                    {{/if}}
                </td>
            </tr>
        {{/for}}
        </tbody>
    </table>
</script>

Solution

  • When you add an {{if}} block, it is a nested view, so the button click is not getting you the item view with the index. You need to use $.view(this).parent.index - or, simpler, $.view(this).getIndex() - which automatically steps up through nested views (if any) to the item view and gets its index.

    app.select($.view(this).getIndex());
    

    (See discussion here: https://github.com/BorisMoore/jsrender/issues/173#issuecomment-11058106)

    BTW here is a modified form of your sample, just to give you some ideas. It uses <button data-link="{on ~root.select #getIndex()}">Edit</button> to hook up the click handler on the button and call the select method directly, passing it the index:

    <script id="myTemplate" type="text/x-jsrender">
    <table id="tblData" class="data">
        <thead>
            ...
        </thead>
        <tbody>
        {^{for data}}
            <tr class="item">
            {^{if ~root.selectedIndex===#index}}
                <td><button class="cancelButton" data-link="{on ~root.select null}">Cancel</button></td>
                <td><input data-link="A" /></td>
                <td><input data-link="B" /></td>
            {{else}}
                <td><button class="editButton" data-link="{on ~root.select #getIndex()}">Edit</button></td>
                <td data-link="A"></td>
                <td data-link="B"></td>
            {{/if}}
            </tr>
        {{/for}}
        </tbody>
    </table>
    </script>
    
    <div id="divHolder"></div>
    
    <script>
    var app = {
        data: [{A:"aa", B: "bb"},{A:"aa2", B: "bb2"}],
        selectedIndex: null,
        select: function(index) {
            if (this.selectedIndex !== index) {
                $.observable(this).setProperty("selectedIndex", index);
            }
        }
    };
    
    $.templates("#myTemplate").link("#divHolder", app);
    </script>