Knockout + Select2 - 设置默认值?

Posted

技术标签:

【中文标题】Knockout + Select2 - 设置默认值?【英文标题】:Knockout + Select2 -- setting default values? 【发布时间】:2013-11-24 20:17:54 【问题描述】:

TL;DR:我需要设置一个默认值以显示在 select2 字段上,通过敲除绑定,但 select2 绑定不断将我的 viewmodel 值覆盖为 "" 而不是接受该值。

成分

我正在使用以下内容:

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

目标

将我的值作为初始视图模型的Start() 函数的一部分加载 将 select2 默认值绑定到我的 VM 在加载发票时的值。 允许用户在选择时选择其他选项 由于我们降低 select2 值的方式,默认值也将包含在 select2 选项中,因此无需

问题

如果我从空白表单开始,Select2 会完全正常工作。它会在下拉列表等时从 Ajax 调用中加载我的值。 但是,当我加载要显示的发票时,未在 select2 控件上设置视图模型值。 看起来 select2 控件实际上加载了数据并在加载时用"" 覆盖了我的视图模型的值,因为尚未选择一个值——而不是让我根据我的绑定值显示一个默认项。 .

目前关于尝试解决它的想法

我将调查所有这些:

我可能没有正确使用敲除绑定来允许不属于其值一部分的默认元素选择。 如果有一种方法可以验证 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;
        
    
;

我们正在使用的淘汰赛绑定

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

【问题讨论】:

【参考方案1】:

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

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

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

【讨论】:

嗨,Artem,感谢您的参与。我会尝试添加一些屏幕截图。我认为问题在于,当通过我的 select2 敲除绑定连接 select2 框时,它会将 viewmodel 值覆盖为 "",而不是从 ViewModel 的 Invoice().TypeID 属性中加载其值。 是的——问题是,它似乎正在返回“[Object object]。在查看它时,我认为部分问题是 valueAccessor 似乎正在返回一个 select2 对象,而不是实际的淘汰赛绑定,尽管我不确定发生了什么。在 chrome 的调试器中,我看到诸如“allowClear:false”和“placeholder:“Invoice Type”之类的属性,它们是 select2 控件的一部分 对于 init 部分我们也应该使用:var val = ko.utils.unwrapObservable(valueAccessor()); $(element).val(val); 因为var obj = valueAccessor() 提供了一个函数。【参考方案2】:

我得到了它的工作,我认为不同之处在于 init 与 select 类似

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

【讨论】:

以上是关于Knockout + Select2 - 设置默认值?的主要内容,如果未能解决你的问题,请参考以下文章

Knockout Select2 按对象设置初始值

Knockout.js Select2 绑定。将 Select2 升级到 v4 后损坏

Knockout/Select2:触发 select2 根据可观察的选项更新进行更新

Knockout.js 与多个 Select2 绑定

带有自定义模板的 Knockout.Js 中的 Select2

Select2 与 Knockout.js 初始值