淘汰验证 - 计算的可观察/下拉列表

时间:2017-04-18 14:01:04

标签: knockout.js knockout-validation

我的淘汰应用程序中有一个下拉列表,标记如下:

查看:

<select data-bind="options: $root.counties, optionsText: 'name', value: county, optionsCaption: '@PlanStrings.ChooseFromDropdownMessage'"></select>

我希望验证这一点,以确保选择一个选项。 它在浏览器中呈现如下:

<select data-bind="options: $root.counties, optionsText: 'name', value: county, optionsCaption: 'Choose...'">
    <option value="">Choose...</option>
    <option value="">Carlow</option>
    <option value="">Cavan</option>
    <option value="">Clare</option>
    <option value="">Cork</option>
    <option value="">Donegal</option>
    <option value="">Dublin</option>
    <option value="">Galway</option>
    <option value="">Kerry</option>
    <option value="">Kildare</option>
    <option value="">Kilkenny</option>
    <option value="">Laois</option>
    <option value="">Leitrim</option>
    <option value="">Limerick</option>
    <option value="">Longford</option>
    <option value="">Louth</option>
    <option value="">Mayo</option>
    <option value="">Meath</option>
    <option value="">Monaghan</option>
    <option value="">Offaly</option>
    <option value="">Roscommon</option>
    <option value="">Sligo</option>
    <option value="">Tipperary</option>
    <option value="">Waterford</option>
    <option value="">Westmeath</option>
    <option value="">Wexford</option>
    <option value="">Wicklow</option>
</select>

值县是一个计算的observable,代码在这里:

/// <summary>Constructor function for site info view model</summary>
function SiteModel(data, parent) {
    var self = this;
    // some code etc
    self.county = ko.computed({
        read: function () {
            return ko.utils.arrayFirst(
                    parent.counties(),
                    i => i.countyId() === self.countyId()
            );
        },
        write: function (value) {
            self.countyId(value === undefined ?
                null : value.countyId());
        }
    });
}

在整个ViewModel()函数中调用SiteModel。

我已在同一页面上的简单输入文本字段(非计算)上实现了ko验证,因此它不是设置ko验证的更基本问题。我只是做错了。验证此计算的observable / dropdown。

我已经尝试了以下所有方法:

self.countyId = ko.observable().extend({ required: { message: "You must select a county." } });
self.county.extend({ required: { message: "You must select a county." } });
self.countyId.extend({ required: { message: "You must select a county." } });
self.county = ko.computed({
    read: function () {
        return ko.utils.arrayFirst(
                parent.counties(),
                i => i.countyId() === self.countyId()
        );
    },
    write: function (value) {
        self.countyId(value === undefined ?
            null : value.countyId());
    }
}).extend({ required: { message: "You must select a county." } });

这些似乎都不起作用。 对此感到不知所措。 关于如何验证此下拉/计算可观察量的任何建议?

我可以补充一点,此下拉列表的其他所有方面都能正常工作,因为它在保存页面时会正确读取值,并且如果模型包含值,则使用模型中的值作为选定值进行渲染。

## EDIT ## 包括完整性的完整View模型代码:

function ($, ko, validation, mapping, formatting, strings, global, base) {

    /// <summary>Constructor function for top-level view model</summary>
    function ViewModel(data) {
        var self = this;
        var map = {
            ignore: ["RegistrationDate"],
            sites: {
                create: function (options) {
                    return new SiteModel(options.data, self);
                }
            }
        };
        mapping.fromJS(data, map, self);

        self.industrySector = ko.computed({
            read: function () {
                return ko.utils.arrayFirst(
                    self.industrySectors(),
                    i => i.industrySectorId() === self.industrySectorId()
                );
            },
            write: function (value) {
                self.industrySectorId(value === undefined ?
                    null : value.industrySectorId());
            }
        });

        self.isEditingNumberOfEmployees = ko.observable(false);
        self.editNumberOfEmployees = () => self.isEditingNumberOfEmployees(true);
        self.numberOfEmployeesFormatted =
            ko.computed(() => {
                var value = ko.unwrap(self.numberOfEmployees);
                return value === null ?
                    strings.nullPlaceholder :
                    value;
            });

        self.isEditingTurnover = ko.observable(false);
        self.editTurnover = () => self.isEditingTurnover(true);
        self.turnoverFormatted =
            ko.computed(() => {
                var value = ko.unwrap(self.turnover);
                if (value !== null)
                {
                    var val_string = value.toString();
                    var value_parsed = parseFloat(val_string.replace(/[^\d\.]/g, ''));
                }
                return value === null ?
                    strings.nullPlaceholder :
                    formatting.formatDecimal(value_parsed);
            });

        self.isEditingYearEstablished = ko.observable(false);
        self.editYearEstablished = () => self.isEditingYearEstablished(true);
        self.yearEstablishedFormatted =
            ko.computed(() => {
                var value = ko.unwrap(self.yearEstablished);
                return value === null ?
                    strings.nullPlaceholder :
                    value;
            });

        self.shouldShowLogoMessage = ko.observable(true);

        var companyLogoId = data.logoFileId;

        if (companyLogoId > 0)
        {
            self.shouldShowLogoMessage(false);
        }


        self.uploadLogo = function (data, event) {
            var inputId = $(event.target).data("inputId");
            $("#" + inputId).trigger("click");
        };

        self.liveSites = ko.computed(function () {
            return ko.utils.arrayFilter(
                self.sites(),
                site => ko.unwrap(site.state) !== "Deleted"
            );
        });

        self.addSite = function () {
            var newSite = new SiteModel(
                {
                    name: null,
                    address1: null,
                    address2: null,
                    address3: null,
                    countyId: null,
                    keyActivities: null,
                    state: "Added"
                },
                self
            );
            self.sites.push(newSite);
            self.setTab(newSite.position());
        }

        self.setTab = function (tabIndex) {
            // FIXME Investigate replacing with KO microtask when upgraded
            // to KO 3.4
            window.setTimeout(function () {
                $("#sites_tab").tabs("option", "active", tabIndex);
            }, 10);
        };

        self.siteCount = ko.computed(() => self.liveSites().length);

        self.dirtyFlag = new ko.dirtyFlag(self, false);

    }

    var knockoutValidationSettings = {
        insertMessages: true,
        messagesOnModified: true,
    };

    ko.validation.init(knockoutValidationSettings, true);

    ViewModel.prototype = base;


    /// <summary>Constructor function for site info view model</summary>
    function SiteModel(data, parent) {
        var self = this;

        mapping.fromJS(data, {}, self);

        self.position = ko.computed(function () {
            return parent.sites().indexOf(self);
        });

        self.hasData = function () {
            return (self.name() !== "" ||
                self.address1() !== "" ||
                self.address2() !== "");
        };

        self.displayName = ko.computed({
            read: function () {
                if (self.name() === "" || self.name() === null) {
                    return strings.introductionSiteAutoName + " " + (parent.liveSites ?
                        parent.liveSites().indexOf(self) + 1 :
                        parent.sites().indexOf(self) + 1).toString();
                }
                return self.name();
            },
            write: function (value) {
                self.name(value);
            }
        });

        self.anchorRef = ko.computed(function () {
            return "#tabs-" + (self.position() + 1).toString();
        });

        self.idRef = ko.computed(function () {
            return "tabs-" + (self.position() + 1).toString();
        });

        self.stateModified = ko.computed(function () {
            self.name();
            self.address1();
            self.address2();

            if (self.state() === "Unchanged") {
                self.state("Modified");
            }
        });

        self.deleteSite = function () {
            var pos = self.position();
            // FIXME: Replace with proper dialog box
            if (window.confirm(strings.introductionConfirmDeleteSiteMessage)) {
                if (self.state() === "Added") {
                    // Site was never saved to DB so just remove it
                    parent.sites().splice(pos, 1);
                }
                self.state("Deleted");
                if (pos > 0) pos--;
                parent.setTab(pos);
            }
        }

        self.county = ko.computed({
            read: function () {
                return ko.utils.arrayFirst(
                        parent.counties(),
                        i => i.countyId() === self.countyId()
                );
            },
            write: function (value) {
                self.countyId(value === undefined ?
                    null : value.countyId());
            }
        }).extend({ required: true });

        self.countyHidden = ko.computed({
            read: function () {
                return ko.utils.arrayFirst(
                        parent.counties(),
                        i => i.countyId() === self.countyId()
                );
            },
            write: function (value) {
                self.countyId(value === undefined ?
                    null : value.countyId());
            }
        }).extend({ required: true });

        self.name.extend({ required: { message: "You must enter a name." } });

    }

    return {
        ViewModel: ViewModel,
    };
}

1 个答案:

答案 0 :(得分:0)

您需要以不同的方式使用select绑定。 (使用optionsValue

将所选值更新为countyId而不是county 那么您可以county获得computed observable取决于countyId

&#13;
&#13;
var data = { counties: [{ countyId: 1, name: 'Carlow' },{ countyId: 2, name: 'Donegal' },{ countyId: 3, name: 'Galway' },{ countyId: 4, name: 'Kildare' },{ countyId: 5, name: 'Laois' }], countyId: 2 };

function vm(data) {
  var self = this;
  self.counties = ko.observable(data.counties);
  self.countyId = ko.observable(data.countyId).extend({ required: true });
  self.contry = ko.computed(function() {
    return ko.utils.arrayFirst(this.counties(), i => i.countyId == this.countyId());
  }, self);
}

ko.applyBindings(new vm(data));
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout-validation/2.0.3/knockout.validation.min.js"></script>
<select data-bind="options: $root.counties, optionsText: 'name', optionsValue: 'countyId', value: countyId, optionsCaption: 'choose..'"></select>
<pre data-bind="text: ko.toJSON($root, null, 2)"></pre>
&#13;
&#13;
&#13;