Reputation: 759
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:
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);
Upvotes: 0
Views: 144
Reputation: 759
I was able to solve the problem by creating other templates.
Here's the code:
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 id="expList">
<li>
<span data-bind="text: text"></span> <span data-bind="text: showGroupItems"></span>
<br />
<ul>
<!-- ko template: { name: renderTemplate, foreach: formItems } -->
<!-- /ko -->
</ul>
</li>
</ul>
<!-- /ko -->
<!-- ko ifnot: showGroupItems -->
<!-- ko template: { name: 'drillDownToSelectedGroup', foreach: formItems } -->
<!-- /ko -->
<!-- /ko -->
</script>
<script id="drillDownToSelectedGroup" type="text/html">
<!-- ko if: isGroup -->
<!-- ko if: def.group.headingLevel == 1 -->
<!-- ko template: { name: renderTemplate } -->
<!-- /ko -->
<!-- /ko -->
<!-- /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) {
if (item.isGroup && item.def.group.headingLevel == 1)
return "group";
else
return "empty";
}
};
// Cascade down changes
self.showGroupItems.subscribe(function (newValue) {
$(self.formItems()).each(function (indx, fi) {
if (fi.isGroup)
fi.showGroupItems(newValue);
})
});
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
},
{
"field": null,
"group": {
"formItems": [
{
"field": {
"paraId": "{value:'2'}"
},
"group": null,
"text": "This is field 2.1",
"ordinal": 1
},
{
"field": {
"paraId": "{value:'3'}"
},
"group": null,
"text": "This is field 2.2",
"ordinal": 2
},
{
"field": null,
"group": {
"formItems": [
{
"field": {
"paraId": "{value:'4'}"
},
"group": null,
"text": "This is field 2.2.1",
"ordinal": 1
}
],
"headingLevel": 1
},
"text": "Group 2.1",
"ordinal": 3
}
],
"headingLevel": 1
},
"text": "Group 2",
"ordinal": 3
}
],
"id": 2,
"text": "Project 1"
};
var viewModel = new FormDef(def);
ko.applyBindings(viewModel);
See final work in this fiddle.
If you find this helpful please don't forget to up-vote.
Upvotes: 0