Note that there are some explanatory texts on larger screens.

plurals
  1. POKnockout + Select2 -- setting default values?
    primarykey
    data
    text
    <p><strong>TL;DR:</strong> I need to set a default value for display on a select2 field, bound via knockout, but the select2 binding keeps overriding my viewmodel value to "" instead of accepting the value.</p> <h2>The Ingredients</h2> <p>I am utilizing the following:</p> <ul> <li>KnockoutJS</li> <li>Select2 on input fields</li> <li>Custom knockout binding to select2</li> <li>An ajax call to load an object (an invoice) as part of a Start() method on my viewmodel.</li> </ul> <h2>The Goal</h2> <ul> <li>Load my values as part of the initial viewmodel's <code>Start()</code> function</li> <li>Bind the select2 default values to the values of my VM at the time it loads the invoice.</li> <li>Allow users to select other options as they choose <ul> <li>the default values wold be included in the select2 options as well due to the way we're bringing down the select2 values, so no need to </li> </ul></li> </ul> <h2>The Problem</h2> <ul> <li>Select2 would be working entirely fine if I was starting from a blank form. It loads my values from an Ajax call upon dropdown, etc.</li> <li>However, when I load the invoice for display, the viewmodel values aren't set on the select2 controls. <ul> <li>It appears the select2 control actually loads the data and overwrites my viewmodel's value with <code>""</code> when it loads, as a value hasn't been selected yet -- rather than letting me show a default item based on my bound value..</li> </ul></li> </ul> <h2>Thoughts so far on Trying to Solve it</h2> <p>I'll be investigating all of these:</p> <ul> <li>I might not be properly using the knockout binding to allow for a default element choice that isn't a part of its values.</li> <li>If there is a way I could verify that the select2 boxes are loaded and then trigger an element update, that would be fine, too.</li> </ul> <h2>The Code</h2> <p><strong>Document Load</strong></p> <pre><code>$(document).ready(function () { 'use strict'; console.log("creating viewmodel"); vm = new invoiceDetailsPage.ViewModel(); vm.Start(); console.log("applying bindings"); ko.applyBindings(vm); }); </code></pre> <p><strong>The invoiceDetailsPage NameSpace(some irrelevant parts removed)</strong></p> <pre><code>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 () { //finalized if it has the appropriate status (anything except) }); self.hasPrivateComments = ko.computed(function () { // if self.privatecomment isn't null or empty, true }); self.TotalFees = ko.computed(function () { //foreach item in detailitems, sum of fees. }); self.TotalCosts = ko.computed(function () { //foreach item in detailitems, sum of Costs. }); self.TotalAdjustments = ko.computed(function () { //foreach item in detailitems, sum of adjustments. }); self.GrandTotal = ko.computed(function () { //foreach item in detailitems, sum of totals. }); }, LoadInvoice: function (clientSiteID, invoiceID, callbackFunction, errorFunction) { 'use strict'; var self = this; self.clientSiteID = clientSiteID; self.invoiceID = invoiceID; $.ajax({ url: '/api/DefenseInvoice/GetDefenseInvoice?ClientSiteID=' + self.clientSiteID + "&amp;InvoiceID=" + invoiceID, type: 'GET', processData: false, contentType: 'application/json; charset=utf-8', dataType: "json", data: null, success: function (data) { console.log(data); callbackFunction(data); }, error: function (jqXHR, textStatus, errorThrown) { errorFunction(jqXHR, textStatus, errorThrown); } }); }, ViewModel: function () { 'use strict'; var self = this; self.InvoiceLoaded = ko.observable(); self.Invoice = ko.observable(new invoiceDetailsPage.Invoice()); // load blank invoice first self.clientSiteID = -1; self.invoiceID = -1; self.SaveInvoiceDetails = function () { // can only save the details prior to approval / rejection // should update only general invoice fields, not private comments or adjustments }; self.LoadInvoice = function() { self.InvoiceLoaded(false); invoiceDetailsPage.LoadInvoice(self.clientSiteID, self.invoiceID, function(result) { //success vm.Invoice(new invoiceDetailsPage.Invoice( result.InvoiceInfo.DefenseInvoiceID, result.InvoiceDocumentTypeID, result.InvoiceInfo.FirmID, result.InvoiceInfo.InvoiceNumber, result.InvoiceInfo.InvoicePeriod, result.InvoiceInfo.DatePayable, result.InvoiceInfo.PrivateComment, result.InvoiceInfo.StatusID, result.InvoiceInfo.VendorFirmID )); self.InvoiceLoaded(true); }, function() { //error toastr.error("We're sorry, but an error occurred while trying to load the invoice. Please contact support or refresh the page to try again.", "Invoice Approval"); console.log("LoadInvoice -- ERROR"); console.log(" error: " + errorThrown); toastr.clear(notifier); }); }; self.Start = function () { self.LoadInvoice(); }; }, utils: { GetSelect2Options: function (placeholder, url) { 'use strict'; var options = { allowClear: false, placeholder: placeholder, query: function (query) { var dto = { query: query.term, filters: { ClientSiteID: Number(vm.clientSiteID) } }; $.ajax({ type: "POST", url: url, data: JSON.stringify(dto), contentType: "application/json; charset=utf-8", dataType: "json", success: function (msg) { query.callback(msg); } }); } }; return options; } } }; </code></pre> <p><strong>The Knockout Binding we're using</strong></p> <pre><code>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'); } }; </code></pre> <p><strong>The HTML Element and its bindings</strong></p> <pre><code>&lt;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"/&gt; </code></pre>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload