My goal is to be able to build a HTML form using a JSOM array as my data source and using Knockout's recursive template feature.
Details:
- I'm working with only one nested JavaScript array.
- My data structure is the following:
- A form contains one item
- An item contains contains a field or a group.
- A group contains items
- Basically the page consists of one DIV which through template recursion generates other DIVs: one contains the groups and the other contains the items. Visually you have two blocks of data on the page.
- The LHS panel displays all the groups configured as heading level 1.
Using recursive template I've got a modest demo version working as you can see here.
My issue: I would like to know how to populate the template on RHS as you click a group on the LHS. Say for example, if I click on Group 1 I expect to see all items for Group 1 including the items for sub-groups (Group 1.1). However, if I click group 1.1 I only want to see the group items for group 1.1. At the moment I cannot even get the items form sub-group 'Group 1.1' to display. The showGroupItems function doesn't return true.
My code is as follows:
HTML
<script id="formDef" type="text/html">
<!--<p>FormDef</p>-->
<div style="float: left; width: 300px;">
<!--<p>formDef</p>-->
<!-- ko template: { name: renderGroup, foreach: formItems } -->
<!-- /ko -->
</div>
<div style="float: left;">
<!-- ko template: { name: 'formElementNodeTemplate', foreach: formItems } -->
<!-- /ko -->
</div>
</script>
<script id="formElementNodeTemplate" type="text/html">
<!-- ko if: showGroupItems -->
<ul data-bind="">
<li>
<span data-bind="text: text"></span>
<br />
<!-- ko template: { name: renderTemplate, foreach: formItems } -->
<!-- /ko -->
</li>
</ul>
<!-- /ko -->
</script>
<script id="group" type="text/html">
<!--<p>Group</p>-->
<span data-bind="text: text, click: $root.selectSection"></span>
<br />
<!-- ko template: { name: renderGroup, foreach: formItems } -->
<!-- /ko -->
</script>
<script id="empty" type="text/html">
</script>
<script id="field" type="text/html">
<!--<p>Paragraph</p>-->
<ul>
<li><span data-bind="text: text"></span></li>
</ul>
</script>
<div data-bind="template: { name: 'formDef', data: $data }"></div>
Script
function SortByOrdinal(a, b) {
return ((a.ordinal < b.ordinal) ? -1 : ((a.ordinal > b.ordinal) ? 1 : 0));
}
var FormGroup = function (formItem) {
var self = this;
self.def = formItem;
self.isGroup = true;
self.text = formItem.text; // title
self.ordinal = formItem.ordinal;
self.showGroupItems = ko.observable(false);
var formItems = [];
$(formItem.group.formItems).each(function (indx, fi) {
if (fi.group != null)
formItems[formItems.length] = new FormGroup(fi);
else
formItems[formItems.length] = new FormField(fi);
});
formItems.sort(SortByOrdinal);
self.formItems = ko.observableArray(formItems);
self.renderTemplate = function (item) {
if (item.isGroup)
return 'formElementNodeTemplate';
else
return 'field';
};
self.renderGroup = function (item) {
if (item.isGroup && item.def.group.headingLevel == 1)
return "group";
else
return "empty";
};
return self;
}
var FormField = function (formItem) {
var self = this;
self.def = formItem;
self.isGroup = formItem.group != null;
self.text = formItem.text;
self.ordinal = formItem.ordinal;
return self;
}
var FormDef = function (formDef) {
var self = this;
self.text = formDef.text;
self.formDef = formDef;
self.showGroupItems = true;
var formItems = [];
$(formDef.formItems).each(function (indx, di) {
if (di.group != null)
formItems[formItems.length] = new FormGroup(di);
else
formItems[formItems.length] = new FormField(di);
});
self.formItems = ko.observableArray(formItems);
self.renderTemplate = function () {
return 'formElementNodeTemplate'
};
self.renderGroup = function (item) {
if (item.isGroup && item.def.group.headingLevel == 1)
return "group";
else
return "empty";
};
self.selectedSection = ko.observable();
self.selectSection = function (item) {
if (self.selectedSection()) self.selectedSection().showGroupItems(false);
item.showGroupItems(true);
self.selectedSection(item);
};
return self;
}
var def = {
"formItems": [
{
"field": null,
"group": {
"formItems": [
{
"field": {
"paraId": "{value:'1'}"
},
"group": null,
"text": "This is field 1.1",
"ordinal": 1
},
{
"field": {
"paraId": "{value:'2'}"
},
"group": null,
"text": "This is field 1.2",
"ordinal": 2
},
{
"field": null,
"group": {
"formItems": [
{
"field": {
"paraId": "{value:'3'}"
},
"group": null,
"text": "This is field 1.1.1",
"ordinal": 1
}
],
"headingLevel": 1
},
"text": "Group 1.1",
"ordinal": 3
}
],
"headingLevel": 1
},
"text": "Group 1",
"ordinal": 2
}
],
"id": 2,
"text": "Project 1"
};
var viewModel = new FormDef(def);
ko.applyBindings(viewModel);