简体   繁体   中英

Knockout JS - Containerless Control Breaks Foreach using Template

Apologies for the long winded title. Hopefully some code will make things clearer.

Before I begin, I am using KnockoutJs version 3.0.0, bootstrap 3 and jquery 1. 7.1. This issue is related to KnockoutJS.

I have the following html which creates a form based upon my underlying javascript model.

        <form role="form" data-bind="foreach: { data: controls, as: 'control' }" >
        <div class="form-group control-form-group" data-bind="attr:{class: $root.cssControlClassColumnWidth }, visible: control.isVisible">
            <fieldset class="control-border" data-bind="
                event:{
                    dragstart: function(control, event) { $root.dragStartControl($parent,control, event) },
                    dragover: function(control, event) { $root.dragOverControl(event) }
                    }">
                <legend class="control-border">
                    <b data-bind="text: control.name"></b>
                    <span class="glyphicon glyphicon-remove-circle btn-control-remove text-danger" data-bind="visible: $root.inCustomisationMode,click: $root.removeControl"></span>
                </legend>
                <div class="form-inline" data-bind="foreach: {data: parameters, as: 'parameter'}">
                    <div class="form-group" >
                        <div data-bind="template: { name: parameter.template, data: parameter }"></div>
                    </div>
                </div>
            </fieldset>
        </div>
    </form>

This works fine. But when I change the code to the following (adding the containerless controls) my inner foreach loop which uses a template determined by the parameter.template value isn't displayed.

        <form role="form" data-bind="foreach: { data: controls, as: 'control' }" >
        <!-- ko if:true -->         
        <div class="row">    
        <!-- /ko -->    
            <div class="form-group control-form-group" data-bind="attr:{class: $root.cssControlClassColumnWidth }, visible: control.isVisible">
                <fieldset class="control-border" data-bind="
                  event:{
                      dragstart: function(control, event) { $root.dragStartControl($parent,control, event) },
                      dragover: function(control, event) { $root.dragOverControl(event) }
                      }">
                    <legend class="control-border">
                        <b data-bind="text: control.name"></b>
                        <span class="glyphicon glyphicon-remove-circle btn-control-remove text-danger" data-bind="visible: $root.inCustomisationMode,click: $root.removeControl"></span>
                    </legend>
                    <div class="form-inline" data-bind="foreach: {data: parameters, as: 'parameter'}">
                        <div class="form-group" >
                            <div data-bind="template: { name: parameter.template, data: parameter }"></div>
                        </div>
                    </div>
                </fieldset>
            </div>
        <!-- ko if:true -->         
        </div>    
        <!-- /ko --> 
    </form>

As you can see, all I want to do is add a row (I will add a variable which will control the number of form-groups I add per row once I get this working). This should be trivial, but my inner foreach cannot seem to use the template (see below for an example template).

    <script type="text/html" id="parameterComboBoxTemplate">
    <label data-bind="text: name"></label>
    <select class="input-sm" data-bind="options: values, value: selectedValue"></select>
</script>

Hopefully someone could shed some light on why I am seeing this odd behaviour.

Let me know if there is anything else I can add to help identify the issue.

Knockout's "containerless control flow syntax" uses comments as "virtual elements". They must still follow element rules. This means that the HTML between the opening and closing tags must be complete elements. So this is how the hierarchy plays out:

<form role="form" data-bind="foreach: { data: controls, as: 'control' }" >
    <!-- ko if:true -->         
        <div class="row">    
            <!-- /ko -->             <--- ignored
            <div class="form-group control-form-group" data-bind="attr:{class: $root.cssControlClassColumnWidth }, visible: control.isVisible">
                ...
            </div>
            <!-- ko if:true -->      <--- will generate an error
        </div>    
    <!-- /ko --> 
</form>

In case anyone was interest in how I solved my issue here is my code:

    <form role="form" data-bind="foreach: { data: controls, as: 'control' }" >
        <!-- We only want to create a row of controls on every even number (We may want to change this
            to have a different number of controls in the row so changing the method name to something 
            more generic would be better -->
        <!-- ko if: $root.isRowEven($index()) -->

            <!-- HERE WE WANT TO ADD TWO CONTROLS TO THE ROW!!-->
            <!-- ko if: $parent.controls[$index() + 1] -->
            <div class="row">
                <div class="form-group control-form-group" data-bind="attr:{class: $root.cssControlClassColumnWidth }, visible: control.isVisible">
                    <fieldset class="control-border" data-bind="
                        event:{
                            dragstart: function(control, event) { $root.dragStartControl($parent,control, event) },
                            dragover: function(control, event) { $root.dragOverControl(event) }
                            }">
                        <legend class="control-border"> 

                            <b data-bind="text: control.name"></b>
                            <span class="glyphicon glyphicon-remove-circle btn-control-remove text-danger" data-bind="visible: $root.inCustomisationMode,click: $root.removeControl"></span>
                        </legend>
                        <div class="form-inline" data-bind="foreach: {data: parameters, as: 'parameter'}">
                            <div class="form-group" >
                                <div data-bind="template: { name: parameter.template, data: parameter }"></div>
                            </div>
                        </div>
                    </fieldset>
                </div>
                <div class="form-group control-form-group" data-bind="attr:{class: $root.cssControlClassColumnWidth }, visible: control.isVisible">
                    <fieldset class="control-border" data-bind="
                        event:{
                            dragstart: function(control, event) { $root.dragStartControl($parent,control, event) },
                            dragover: function(control, event) { $root.dragOverControl(event) }
                            }">
                        <legend class="control-border">                   
                            <b data-bind="text: $parent.controls[$index() + 1].name"></b>
                            <span class="glyphicon glyphicon-remove-circle btn-control-remove text-danger" data-bind="visible: $root.inCustomisationMode,click: $root.removeControl"></span>
                        </legend>
                        <div class="form-inline" data-bind="foreach: {data: $parent.controls[$index() + 1].parameters, as: 'parameter'}">
                            <div class="form-group" >
                                <div data-bind="template: { name: parameter.template, data: parameter }"></div>
                            </div>
                        </div>
                    </fieldset>
                </div>
            </div>
            <!-- /ko --> 


            <!-- HERE WE WANT TO ADD ONE CONTROL TO THE ROW!!-->
            <!-- ko if: !$parent.controls[$index() + 1] -->
            <div class="row">
                <div class="form-group control-form-group" data-bind="attr:{class: $root.cssControlClassColumnWidth }, visible: control.isVisible">
                    <fieldset class="control-border" data-bind="
                        event:{
                            dragstart: function(control, event) { $root.dragStartControl($parent,control, event) },
                            dragover: function(control, event) { $root.dragOverControl(event) }
                            }">
                        <legend class="control-border"> 
                            <b data-bind="text: control.name"></b>
                            <span class="glyphicon glyphicon-remove-circle btn-control-remove text-danger" data-bind="visible: $root.inCustomisationMode,click: $root.removeControl"></span>
                        </legend>
                        <div class="form-inline" data-bind="foreach: {data: parameters, as: 'parameter'}">
                            <div class="form-group" >
                                <div data-bind="template: { name: parameter.template, data: parameter }"></div>
                            </div>
                        </div>
                    </fieldset>
                </div>
            </div>
            <!-- /ko --> 

        <!-- /ko -->  




    </form>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM