TL DR: I need to set a default value for display in the select2 field associated with a knockout, but the select2 binding retains the value of my viewmodel instead of "" instead of accepting the value.
Ingredients
I am using the following:
- Knockoutjs
- Select2 for input fields
- Custom knockout binding for select2
- Ajax call to load an object (invoice) as part of the Start () method on my view model.
goal
- Download my values ββas part of the original viewmodel
Start() function - Bind the default select2 values ββto the values ββof my virtual machine at the time of loading the invoice.
- Allow users to select other options as they wish.
- default wold values ββwill also be included in select2 options due to the way we lower select2 values ββso no need to
Problem
- Select2 will work fine if I start with an empty form. It loads my values ββfrom an Ajax call with a dropdown, etc.
- However, when I load an invoice for display, viewmodel values ββare not set on select2 elements.
- The select2 control seems to actually load the data and overwrite my viewmodel with
"" when it loads, since the value has not been selected yet - instead of letting me show the default element based on my associated value ..
Thoughts are still about trying to solve it
I will learn all of this:
- I cannot correctly use the knockout binding to allow the selection of a default item that is not part of its values.
- If there is a way, I can check that select2 fields are loaded, and then start updating the item, this will be fine too.
Code
Download document
$(document).ready(function () { 'use strict'; console.log("creating viewmodel"); vm = new invoiceDetailsPage.ViewModel(); vm.Start(); console.log("applying bindings"); ko.applyBindings(vm); });
InvoiceDetailsPage NameSpace (some unnecessary parts removed)
var invoiceDetailsPage = invoiceDetailsPage || { InvoiceDetailItem: function () { 'use strict'; var self = this; self.DatePayable = new Date(); self.Fees = 0.00; self.Costs = 0.00; self.Adjustments = ko.observable(); self.AdjustmentNote = ko.observable(); self.Total = ko.computed(function () { }); self.hasAdjustments = ko.computed(function () { }); }, Invoice: function (invoiceID, documentTypeID, firmID, invoiceNumber, invoicePeriod, datePayable, privateComment, statusID, vendorFirmID) { 'use strict'; var self = this; self.TypeID = ko.observable(documentTypeID); self.PrivateComment = ko.observable(privateComment); self.Status = ko.observable(statusID); self.FirmID = ko.observable(firmID); self.VendorFirmID = ko.observable(vendorFirmID); self.InvoicePeriod = ko.observable(invoicePeriod); self.DatePayable = ko.observable(datePayable); self.InvoiceNumbers = ko.observable(invoiceNumber); self.DetailItems = ko.observableArray([]); self.isFinalized = ko.computed(function () {
The knockout binding we use
ko.bindingHandlers.select2 = { init: function (element, valueAccessor, allBindingsAccessor) { var obj = valueAccessor(), allBindings = allBindingsAccessor(), lookupKey = allBindings.lookupKey; $(element).select2(obj); if (lookupKey) { var value = ko.utils.unwrapObservable(allBindings.value); $(element).select2('data', ko.utils.arrayFirst(obj.data.results, function (item) { return item[lookupKey] === value; })); } ko.utils.domNodeDisposal.addDisposeCallback(element, function () { $(element).select2('destroy'); }); }, update: function (element) { $(element).trigger('change'); } };
HTML element and its bindings
<input type="text" id="ddlInvoiceType" placeholder="Invoice Type" class="select2-container" data-bind="select2: invoiceDetailsPage.utils.GetSelect2Options('Invoice Type', '/api/DefenseInvoiceType/Post'), value: Invoice().TypeID"/>