rlcrews
rlcrews

Reputation: 3562

how to data-bind knockout js viewmodel to jquery dynamically created rows

Within my page which is bound to the viewmodel I have a section where the user can add elements to the page The elements are a filter section comprised of a few value fields and a select element that needs to be databound to the viewmodel.

Running the select option inline in the html it works fine. However once I pull it out and add it to the jquery that adds the filter control it fails to bind. I realize this is due in part to the view model already being bound and I have tried to call apply bindings again but this fails as well.

I can't use the foreach as I have in other areas because this section is optional. Users do not have to add filters if they don't want to which is why I went the jquery route.

Can anyone provide some suggestions on how to re-bind the select element to the view model once the page has already rendered or is there a better approach using just Knockout to accomplish this?

code: html

  <div id="filterSection" data-bind="with: ReportObject">
                <select data-bind="options: SelectedAttributes(), optionsText: function(SelectedAttributes){ return SelectedAttributes.NameHierarchy() + '.' + SelectedAttributes.LabelName() },  optionsCaption:'Select a Field...'">
                </select><!-- the select here works I added this for testing -->
                <div id="filterBuilder">
                    <input type="button" value="Add A Filter" title="Add A Filter" class="special" id="add" /> 
                </div>
            </div>

jQuery

function CreateFilterRow() {
        $("<div class='filterrow'><select class='queryterm' name='condition'><option  value='AND'>AND</option><option value='OR'>OR</option></select>" +
    "<select data-bind='options: SelectedAttributes(), optionsCaption:'Select a Field...'></select>&nbsp;&nbsp;<select name='operator'class='operClass'><option value='EQUALS' id='opt1'>Equals</option><option id='opt1' value='CONTAINS'>Contains</option><option id='opt2' value='DOES NOT CONTAIN'>Does Not Contain</option><option id='opt3' value='LIKE'>Like</option><option id='opt4' value='BETWEEN'>Between</option></select>&nbsp;&nbsp;<input type='text' class='queryterm' name='fieldValue1' id='fieldvalue1' value='' size='25' />&nbsp;&nbsp;<a id='btnRemove' class='ui-button-text-only'>Remove</a><a id='btnadd' class='ui-button-text-only'>Add</a></div>").appendTo('#filterBuilder');
    };

    //orignal add row button
    $(document.body).on('click', '#add', function (event) {
        CreateFilterRow();
        ko.applyBindings(ReportWriterViewModel);
    });

The jquery block here is just the insertion of fields into the DOM and the event click. The apply bindings is invoked further up the line. I omitted the KO code as that works everywhere else so I know the issue is not in the Vm but in injecting these elements post VM initalization.

I have also tried to use the applyBindings and using the dom element name with no luck

 ko.applyBindings(ReportWriterViewModel , document.getElementById('filterSection'));

Thanks in advance,

-cheers

Upvotes: 1

Views: 696

Answers (1)

Cyanfish
Cyanfish

Reputation: 4153

I would indeed use a foreach binding. After all, the observable array can have zero entries if the user hasn't clicked "Add" yet. Knockout is designed to handle this kind of dynamic UI.

Use something like this on your view model:

ReportObject.filterRows = ko.observableArray();
ReportObject.addFilterRow = function () {
    ReportObject.filterRows.push({});
};
ReportObject.removeFilterRow = function (filterRow) {
    ReportObject.filterRows.remove(filterRow);
}

And something like this in your HTML:

<div id="filterSection" data-bind="with: ReportObject">
    <div data-bind="foreach: filterRows">
        <div class="filterrow">
            <!-- put the selects and inputs here -->
            <a data-bind="click: $parent.removeFilterRow" class='ui-button-text-only'>Remove</a>
        </div>
    </div>
    <div id="filterBuilder">
        <input data-bind="click: addFilterRow" type="button" value="Add A Filter" title="Add A Filter" class="special" /> 
    </div>
</div>

Upvotes: 2

Related Questions