Knockout + Select2 - 设置默认值?

时间:2013-11-12 15:34:10

标签: javascript jquery ajax knockout.js jquery-select2

TL; DR:我需要在select2字段上设置显示的默认值,通过knockout绑定,但是select2绑定会将我的viewmodel值覆盖为“”,而不是接受该值。

成分

我正在使用以下内容:

  • KnockoutJS
  • 在输入字段上选择2
  • 自定义淘汰赛绑定到select2
  • ajax调用加载对象(发票)作为我的viewmodel上的Start()方法的一部分。

目标

  • 将我的值加载为初始viewmodel的Start()函数
  • 的一部分
  • 在加载发票时将select2默认值绑定到我的VM的值。
  • 允许用户根据自己的选择选择其他选项
    • 默认值也会包含在select2选项中,因为我们将关闭select2值的方式,所以不需要

问题

    如果我从空白表单开始,
  • Select2将完全正常工作。它在下拉等时从Ajax调用加载我的值
  • 但是,当我加载发票以进行显示时,未在select2控件上设置viewmodel值。
    • 当加载时,select2控件实际上会加载数据并用""覆盖我的viewmodel的值,因为尚未选择值 - 而不是让我根据我的界限显示默认项值..

到目前为止关于试图解决它的想法

我将调查所有这些:

  • 我可能没有正确使用knockout绑定来允许默认元素选择不属于其值。
  • 如果有办法我可以验证是否加载了select2框,然后触发元素更新,那也没关系。

守则

文档加载

$(document).ready(function () {
    'use strict';

    console.log("creating viewmodel");
    vm = new invoiceDetailsPage.ViewModel();
    vm.Start();

    console.log("applying bindings");
    ko.applyBindings(vm);
});

invoiceDetailsPage NameSpace(删除了一些不相关的部分)

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;
        }
    }
};

我们正在使用的Knockout Binding

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元素及其绑定

<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"/>

2 个答案:

答案 0 :(得分:1)

我不确定我是否正确理解了这个问题,起初我在自定义绑定的更新部分看到了可能存在的问题:

update: function (element, valueAccessor) {
  //added next row to update value
  $(element).val(ko.utils.unwrapObservable(valueAccessor()));

  $(element).trigger("change");
}

答案 1 :(得分:1)

我得到了它的工作,我认为区别在于init和一个像

这样的选择
<input type="hidden" class=""
    data-bind="value: observedValue, select2: --your options--">

这是我的:

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);
},