类似于MVC 3 - Ajax.BeginForm 做了一个完整的回发,但我已经验证了对 jquery.unobtrusive-ajax.min.js 的引用确实存在于我的页面中。另外,它在 IE 9 中运行良好,但在 Firefox 和 Chrome 中,我只获得作为完整网页的部分视图,而不是用该部分视图的内容替换 div。
我对 MVC3 还是很陌生,所以我确信这很简单。
概要:
在我的页面中,有几个不同的 .cshtml 文件在用户单击链接时变得可见。在 EquipmentData.cshtml 文件中,我有一个下拉列表,它在更改时会向控制器执行 ajax 回发,以将编辑器模板加载到 div 中。
在该编辑器模板 (StorageTankData.cshtml) 中,有一个数据列表和一个用于单个项目 (StorageTank.cshtml) 的编辑模板的占位符。StorageTank.cshtml 被初始化以插入一个新的(id 为零);当用户单击数据列表中的一项时,我们再次调用控制器以获取该特定项的 StorageTank.cshtml 部分视图并将该 html 替换为编辑 div。
所有这些都很好。在 StorageTank.cshtml 模板中,我有一个 Ajax.Begin 表单,它发布到不同的控制器操作以保存储罐数据,然后调用相同的控制器方法来获取 StorageTankData.cshtml 部分视图。我们的想法是我们将刷新项目列表并将编辑表单重置为插入模式并将其替换到 div 中。
这在 IE 中运行良好,但在 Chrome 和 Firefox 中,我得到的只是页面上的部分视图,而不是整个页面,替换发生了。我认为这是因为我的保存回发与生成整个页面的控制器方法不同,但我不确定该怎么做。
我在 IE 中看到的是 URL 停留在“/Edit”(控制整个页面的操作)上,并且替换效果很好。在 Chrome 和 FF 中,网址改为我的“/SaveStorageTank”操作。所以,我认为这解释了为什么那些浏览器在完成后只显示部分视图。我不明白的是为什么他们不继续进行替换。
在重新显示之前,我在 Firebug 或 Chrome 中都看不到错误。(当然,他们被 $(document).ready... 东西绊倒了,因为该部分视图没有加载 jquery 文件)。但是,也许我只是不知道在哪里/如何查找这些错误。
好吧,对不起,长篇大论。这里有一些细节。
EquipmentEdit.cshtml
@model kpa.mko.bll.viewmodels.clientData.ClientDataFormViewModel
<table id="equipment">
<tr class="form-row">
<td class="form-left">Forklift On Site?</td>
<td class="form-right">
@Html.EditorFor(x => x.ForkliftOnSite)
@Html.ValidationMessageFor(x => x.ForkliftOnSite)
</td>
</tr>
<tr class="form-row">
<td class="form-left">Equipment</td>
<td class="form-right">
@Html.HiddenFor(m => m.AccountId)
Equipment Type: <select id="ddlEquipmentType">
<option value="">--select one--</option>
@foreach (var lookupValue in Model.EquipmentTypes.OrderBy(lv => lv.ShortName))
{
<option value="@lookupValue.LookupValueId">@lookupValue.ShortName</option>
}
</select>
<div id="divEquipmentEdit" style="display: none;">
</div>
</td>
</tr>
</table>
<script type="text/javascript">
$(document).ready(function () {
$("#ddlEquipmentType").change(
function () {
var selection = $("#ddlEquipmentType option:selected").text();
if (selection == "Storage Tanks") {
//Try to get the partial view of the editor template }
$.ajax({
url: '@Url.Action("LoadStorageTankDataEditor")',
type: "POST",
contentType: "application/json; charset=utf-8",
data: JSON.stringify({ accountId: $('#AccountId').val() }),
dataType: "html",
success: function (view) {
$('#divEquipmentEdit').show();
$('#divEquipmentEdit').html(view);
},
error: function (xhr, ajaxOptions, thrownError) {
alert('error = ' + thrownError);
alert('xhr resp text = ' + xhr.responseText);
}
});
} else {
$('#divEquipmentEdit').hide();
}
}
);
});
</script>
StorageTankData.cshtml
@model kpa.mko.bll.viewmodels.clientData.StorageTankData
@using kpa.mko.dal.Entities;
Existing Tanks:
<br /><br />
@Html.HiddenFor(m => m.AccountId)
@if (Model.StorageTanks.Count == 0)
{
<div>No tanks for this account.</div>
}
else
{
foreach (StorageTank st in Model.StorageTanks)
{
@st.ListDescription <a href="javascript:getEditor(@st.StorageTankID);">Edit</a><br />
}
}
<br /><br />
<div id="divTankEditor">
</div>
<script type="text/javascript">
$(document).ready(function () {
getEditor(0);
});
function getEditor(storageTankId) {
//Substitute in the StorageTank editor to the div above.
$.ajax({
url: '@Url.Action("LoadStorageTankEditor")',
type: "POST",
contentType: "application/json; charset=utf-8",
data: JSON.stringify({ storageTankId: storageTankId, accountId: $('#AccountId').val() }),
dataType: "html",
success: function (view) {
$('#divTankEditor').html(view);
if (storageTankId == 0) {
$('#btnTankDelete').hide();
} else {
$('#btnTankDelete').show();
}
},
error: function (xhr, ajaxOptions, thrownError) {
alert('error = ' + thrownError);
alert('xhr resp text = ' + xhr.responseText);
}
});
}
</script>
StorageTank.cshtml
@model kpa.mko.bll.viewmodels.clientData.StorageTankForEdit
@using kpa.mko.dal.Entities;
@using kpa.mko.bll.factories;
@using (Ajax.BeginForm("SaveStorageTank", "ClientData", new AjaxOptions { InsertionMode = InsertionMode.Replace, UpdateTargetId = "divEquipmentEdit" }))
{
@Html.HiddenFor(st => st.TankForEdit.StorageTankID)
<div>Tank Placement:@Html.DropDownListFor(m => m.PlacementSelected, Model.Placements)</div>
<div>Department: @Html.DropDownListFor(m => m.DepartmentSelected, Model.Departments)</div>
<div>Volume (gal): @Html.EditorFor(m => m.TankForEdit.VolumeInGallons)</div>
<div>Content of Tank: @Html.DropDownListFor(m => m.StorageTankContentsSelected, Model.StorageTankContents)</div>
<div>Secondary Containment? @Html.EditorFor(m => m.TankForEdit.HasSecondaryContainment)</div>
<div>If so, what type: @Html.EditorFor(m => m.TankForEdit.SecondaryContainmentType)</div>
<div>Tank Has Overfill Alarm? @Html.EditorFor(m => m.TankForEdit.HasOverfillAlarm)</div>
<div>If so, what type: @Html.EditorFor(m => m.TankForEdit.OverfillAlarmType)</div>
<input type="submit" value="Save Tank Data" name="submitButton" /> <input type="submit" value="Delete Tank" id="btnTankDelete" name="submitButton" /> <input type="button" value="Reset" onclick="getEditor(0); return false;" />
}
控制器保存方法
public PartialViewResult SaveStorageTank(string submitButton, string accountId)
{
int acctId = int.Parse(accountId);
StorageTankForEdit stfe = new StorageTankForEdit(acctId);
if (TryUpdateModel(stfe))
{
if (!string.IsNullOrEmpty(submitButton) && submitButton.Equals("Delete Tank"))
{
//Delete the tank already.
StorageTankFactory.Delete(stfe.TankForEdit);
}
else
{
stfe.TankForEdit.DepartmentFk = int.Parse(stfe.DepartmentSelected);
stfe.TankForEdit.Placement = (StorageTankPlacement)Enum.Parse(typeof(StorageTankPlacement), stfe.PlacementSelected);
stfe.TankForEdit.StorageTankContentFk = int.Parse(stfe.StorageTankContentsSelected);
//@^*#$^ NHibernate doesn't bother to get the content class upon re-display, so we don't see the content type.
//I think it's caching this tank, so let's try populating the content here and see if it shows up.
stfe.TankForEdit.StorageTankContent = StorageTankContentFactory.GetById(stfe.TankForEdit.StorageTankContentFk);
StorageTankFactory.Save(stfe.TankForEdit);
}
}
//We decided to reset to the insert state.
return LoadStorageTankDataEditor(acctId);
获取 StorageTankData 局部视图的控制器方法
public PartialViewResult LoadStorageTankDataEditor(int accountId)
{
StorageTankData std = new StorageTankData(accountId);
std.StorageTanks = StorageTankFactory.GetByAccount(accountId);
return PartialView("EditorTemplates/StorageTankData", std);
}
获取 StorageTank 局部视图的控制器方法
public PartialViewResult LoadStorageTankEditor(int storageTankId, int accountId)
{
StorageTankForEdit st = new StorageTankForEdit(accountId);
if (storageTankId > 0)
{
st.TankForEdit = StorageTankFactory.GetById(storageTankId);
}
return PartialView("EditorTemplates/StorageTank", st);
}