我的淘汰应用程序中有一个下拉列表,标记如下:
查看:
<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,
};
}
答案 0 :(得分:0)
您需要以不同的方式使用select
绑定。 (使用optionsValue
)
将所选值更新为countyId
而不是county
那么您可以county
获得computed observable
取决于countyId
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;