Knockout + Select2 - setting defaults?

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 () { //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 + "&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; } } }; 

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"/> 
+6
source share
2 answers

I'm not sure that I understood the question correctly, first I see a possible problem in terms of updating the user binding:

 update: function (element, valueAccessor) { //added next row to update value $(element).val(ko.utils.unwrapObservable(valueAccessor())); $(element).trigger("change"); } 
+1
source

I got it working, and I think the difference is in init with the choice, for example

 <input type="hidden" class="" data-bind="value: observedValue, select2: --your options--"> 

Here is my:

 init: function(element, valueAccessor, allBindings, viewModel, bindingContext) { ko.utils.domNodeDisposal.addDisposeCallback(element, function() { $(element).select2('destroy'); }); select2 = ko.utils.unwrapObservable(allBindings().select2); $(element).select2(select2); }, 
+1
source

Source: https://habr.com/ru/post/957929/


All Articles