Recursive templating with the KnockoutJs mapping p

2019-05-19 01:50发布

问题:

I am trying to do recursive templating on a tree using the ko.mapping plugin, but I can't get to have it rendered, unless I define separate templates for each level.

In the following case, I want to reuse the mvvmTreeViewGroupTemplate for mvvmTreeViewSubGroups as well, but this is not being rendered, is this a bug or not implemented feature?

<div id="treeViewArea">
    <ul data-bind='template: { 
                            name: "mvvmTreeViewGroupTemplate", 
                            foreach: MvvmTreeItemGroups,
                            beforeRemove: function(elem) { $(elem).slideUp() },
                            afterAdd: function(elem) { $(elem).hide().slideDown() } 
                            }'>
    </ul>
</div>
        <script type="text/x-jquery-tmpl" id="mvvmTreeViewGroupTemplate">  <li> 
            <span data-bind="text: Title" class="mvvmTreeItemStyle"/></br/>     <ul data-bind='template: { 
                                    name: "mvvmTreeViewItemTemplate", 
                                    foreach: MvvmTreeItems, 
                                    beforeRemove: function(elem) { $  (elem).slideUp() }, 
                                    afterAdd: function(elem) { $  (elem).hide().slideDown() } 
                                    }'> 
            <ul data-bind='template: { 
                                    name: "mvvmTreeViewSubGroupTemplate", 
                                    foreach: this.MvvmTreeItemSubGroups, 
                                    beforeRemove: function(elem) { $  (elem).slideUp() }, 
                                    afterAdd: function(elem) { $  (elem).hide().slideDown() } 
                                    }'> 
            </ul> 
            </ul>  
    </li>  
    </script>
    <script type="text/x-jquery-tmpl" id="mvvmTreeViewSubGroupTemplate">  <li> 
            <span data-bind="text: Title" class="mvvmTreeItemStyle"/></br/>     
<ul data-bind='template: { 

                                    name: "mvvmTreeViewItemTemplate", 
                                    foreach: MvvmTreeItems, 
                                    beforeRemove: function(elem) { $  (elem).slideUp() }, 
                                    afterAdd: function(elem) { $  (elem).hide().slideDown() } 
                                    }'> 
            </ul>
      </li>  
    </script>

JSON and script looks like this,

var data = { 
            MvvmTreeItemGroups: [ 
            { 
                Id: 1, Title: 'Group 1', 
                MvvmTreeItemSubGroups: [{ 
                    Id: 1, Title: 'Group 11', 
                    MvvmTreeItems: [{ Id: 'i111', Title: 'Item 111' }, 
{ Id: 'i112', Title: 'Item 112'}] 
                }, 
                    { 
                        Id: 1, Title: 'Group 121', 
                        MvvmTreeItems: [{ Id: 'i121', Title: 'Item 
121' }, { Id: 'i122', Title: 'Item 122'}] 
                    }], 
                MvvmTreeItems: [{ Id: 'i11', Title: 'Item 11' }, { Id: 
'i12', Title: 'Item 12'}] 
            }, 
                { 
                    Id: 2, Title: 'Group 2', 
                    MvvmTreeItemSubGroups: [{ 
                        Id: 1, Title: 'Group 211', 
                        MvvmTreeItems: [{ Id: 'i211', Title: 'Item 
211' }, { Id: 'i212', Title: 'Item 212'}] 
                    }, 
                    { 
                        Id: 1, Title: 'Group 121', 
                        MvvmTreeItems: [{ Id: 'i121', Title: 'Item 
121' }, { Id: 'i122', Title: 'Item 122'}] 
                    }], 
                    MvvmTreeItems: [{ Id: 'i21', Title: 'Item 21' }, 
{ Id: 'i22', Title: 'Item 22'}] 
                }] 
        }; 

var viewModel = ko.mapping.fromJS(data);
        console.log(viewModel);
        ko.applyBindings(viewModel, treeViewArea);

回答1:

I got an answer to my question in this google thread. It's not exactly a 'recursive templating' issue, it's because the template doesn't know how to render if there is no array by that name.

There are two ways to fix the issue:

  1. Check if MvvmTreeItemGroups array actually exists before rendering the template like so,

    {{if $data.MvvmTreeItemGroups }}       
        <ul data-bind='template: { 
        name: "mvvmTreeViewGroupTemplate", 
        foreach: MvvmTreeItemGroups }'> 
        </ul>                 
    {{/if}}
    
  2. Use the in keyword to check MvvmTreeItemGroups really exists

    {if 'MvvmTreeItemGroups' in $data}}
        <ul data-bind='template: { 
        name: "mvvmTreeViewGroupTemplate", 
        foreach: MvvmTreeItemGroups
        }'> 
        </ul> 
    {{/if}}
    

The full fiddle is at http://jsfiddle.net/mikekidder/Xs7sy/