Backstory:我正在开发我的第一个ASP.NET MVC 3应用程序,并希望为客户提供两个类似的页面 - 创建和编辑。 两个页面都有两个DropDownList选择器,允许客户首先选择一种锥形或盘形,然后选择一个子类型 基于第一选择(例如,客户选择'华夫饼锥',然后呈现子类型,如'巧克力浸渍', '普通'等。)。
我正在关注ConeDish
选择器上的'change'事件,以调用Controller操作来返回相关的子类型。像这样:
查看
<td>@Html.DropDownListFor(model => model.TypeID, new SelectList(Model.ConeDishTypes, "TypeID", "Name"), "-- Select Cone or Dish --", new { @id = "conedishselector" })
</td>
<td>@Html.DropDownListFor(model => model.SubtypeID, Enumerable.Empty<SelectListItem>(), "-- How Big? --", new { @id = "subtypeselector" })
</td>
我的“创建”页面的文档就绪处理程序如下所示:
<script type="text/javascript">
$(document).ready(function () {
$('#conedishselector').change(function () {
var selectedTypeID = $(this).val();
if (selectedTypeID != null) {
$.getJSON('@Url.Action("SubtypesForType")', { typeID: selectedTypeID }, function (subtypes) {
var subtypesSelect = $('#subtypeselector');
subtypesSelect.empty();
subtypesSelect.append($('<option/>', { value: 0, text: '-- How Big? --' }));
$.each(subtypes, function (index, subtype) {
subtypesSelect.append($('<option/>', {
value: subtype.SubtypeID,
text: subtype.Name
}));
});
});
}
});
});
</script>
控制器
我有一个看起来像这样的控制器动作:
public ActionResult SubtypesForType(int typeID)
{
using (var db = new IceCreamEntities())
{
IEnumerable<Subtypes> subtypes = db.Subtypes.Where(m => m.TypeID == typeID).ToList();
List<SubtypeIdentifierViewModel> subtypeVMs = new List<SubtypeIdentifierViewModel>();
foreach (var subtype in subtypes)
{
subtypeVMs.Add(new SubtypeIdentifierViewModel(subtype.SubtypeID, subtype.Name, subtype.Size));
}
return Json(subtypeVMs, JsonRequestBehavior.AllowGet);
}
}
基本上,它只检索具有匹配的TypeID的子类型。 我不认为视图模型的结构与讨论密切相关,因此省略了节省空间。
现在,创建的这个位似乎运行得很好。可能有一些方法可以改进它(我很乐意听到),但它实现了我的目标。
但是对于编辑页面,我在使UI显示之前选择的子类型时遇到了一些困难。我当前的脚本代码如下:
<script type="text/javascript">
$(document).ready(function () {
$('#conedishselector').change(function () {
var selectedTypeID = $(this).val();
if (selectedTypeID != null) {
$.getJSON('@Url.Action("SubtypesForType")', { typeID: selectedTypeID }, function (subtypes) {
var subtypesSelect = $('#subtypeselector');
subtypesSelect.empty();
subtypesSelect.append($('<option/>', { value: 0, text: '-- How Big? --' }));
$.each(subtypes, function (index, subtype) {
subtypesSelect.append($('<option/>', {
value: subtype.SubtypeID,
text: subtype.Name
}));
});
});
}
});
$('#conedishselector').trigger("change");
var theVal = $("#hiddensubtypeid").val();
$("#debug").val(theVal);
if (theVal != null && theVal != "") {
// alert here just to check
alert("I got here.");
$('#subtypeselector').val(theVal);
// I also tried this, but that didn't work without the alert either - didn't think it would though
// $("#subtypeselector option[value=" + val + "]").attr("selected", "selected");
}
});
</script>
基本上,页面上的hiddensubtypeid
字段包含之前为此处编辑的冰淇淋选择的ID,并且脚本使用该值来设置选择器,该选择器填充了相应的子类型,如{ {1}}页面脚本。
在这里,我可能不会以“最佳实践”甚至是好方式(即使用隐藏字段或通过文档就绪处理程序内的触发器调用更改)来执行此操作。但我不知道如何不这样做。
有了这一切,我遇到的问题是我认为是时间问题。这个javascript位肯定会获得与先前指定的圆锥或盘子相关联的子类型(此位未显示但视图模型传入其中) 并使用与所选类型相关联的选项填充DropDownList但没有该调试警报(我只在代码中放入,因为我不确定我是否进入条件),子类型的设置不会发生。 / p>
不可否认,我可能会错误地接近这个问题而且我怀疑我的同步大脑期望异步世界以我的大脑看待这个问题的方式工作,所以我很感激任何有关解决这个问题和改进我的设计的建议。我遗漏了一些视图和模型代码,主要是因为我认为它不是讨论的核心。
来自“This Feels Hacky”部门的更多信息:
如果我尝试使用Create
来延迟这样的事情:
setTimeout
事情会奏效,但我认为这是充其量解决问题的胶带解决方案。
解决方案:
所以我相信这一切都归结为$('#conedishselector').trigger("change");
$(function($) {
setTimeout(function() {
var theVal = $("#hiddensubtypeid").val();
$("#debug").val(theVal);
if (theVal != null && theVal != "") {
// alert here just to check
alert("I got here.");
$('#subtypeselector').val(theVal);
}
}, 200); // or 500, or 1000
});
是异步调用(Reference)这一事实,而且从文档中可以清楚地看到,因为它有成功回调,如果请求成功,将在以后的某个时间点执行。那是什么意思?这意味着通过文档就绪函数,调用getJSON
然后立即转身并期望$('#yourfavoriteselector').trigger("change");
的结果完成是错误的逻辑。正如我在上面通过getJSON
所做的那样等待是一个黑客攻击,并导致问题的次优“解决方案”。它依赖(很糟糕,我可能会补充)关于成功回调在等待500ms或指定的任何时间后完成其事情的概念。
我重新组织了这个函数:
setTimeout
并且,正如Badger建议的那样,将选择作为成功回调的一部分,这确保了子类型已被填充。
这样做的好处还在于用户可以将第一个DropDownList的选择更改为某个新值(例如,如果她想查看我们提供的其他锥体/餐具)并将其设置回原始值再次。当发生这种情况时,隐藏字段仍然保留原始主类型的原始子类型,并且回调代码相应地重置子类型DropDownList。因此,用户不必再重新选择她的碟子大小或华夫饼锥型。这对我认为的用户来说只是一点额外的尊重,并且提出了一个很好的副作用。
所以这是'解决方案',也许不是最好的解决方案,但我想说明它的完整性。
答案 0 :(得分:1)
我认为你的问题确实是时机。您正在发送数据请求,然后尝试设置所选项目而不等待JSON响应。
除非有特殊原因,否则我将以不同方式实现getJSON成功函数。
成功时,加载子类型时,您可以检查其中一个是否是您的hiddensubtypeid。如果是,则选择它,否则保留默认值。
如果你想要你可以在成功功能被“消耗”时清除hiddensubtypeid,尽管如果用户选择相关的主要用户体验,总是跳到当前选择的项目可能是有意义的类型。