0

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:

  1. I'm working with only one nested JavaScript array.
  2. My data structure is the following:
    • A form contains one item
    • An item contains contains a field or a group.
    • A group contains items
  3. 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.
  4. 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);
3
  • may be this example will help you or this. Commented Oct 17, 2014 at 6:24
  • I can see what's happening but still haven't worked out the solution: when I click Group 1.1 on the LHS nothing shows because the parent node Group has the attribute showGroupItems set to false. Because it is false the children node, which is Group 1.1 is not displayed even though the showGroupItems entry is set to true. This is tricky because I still want to show the hierarchy (if there is one) but from a node which may not be a parent node. Commented Oct 17, 2014 at 21:23
  • You can the behaviour here When I click Group 1.1 I only want to see Group 1.1 nodes and below. Commented Oct 17, 2014 at 21:32

1 Answer 1

0

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.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.