当页面有多个表单时,在回发后恢复活动的 bootstrap 选项卡
Restore the active bootstrap tab after postback when page has multiple forms
我有一个 ASP.Net MVC 5 项目,它有一个包含 2 个 bootstrap 选项卡的页面。每个选项卡有两个表单,页面上总共有 4 个表单。我的视图模型具有 4 种形式中所有字段的超集。
当我 post 表单时,我希望响应视图显示与 post 编辑表单时显示的相同选项卡。这个 SO 答案 Remain bootstrap tab after postback c# 表明我应该在隐藏字段中设置活动选项卡,return 它在视图模型中,然后在 post 上恢复它。
这个 SO 答案 asp.net MVC 4 multiple post via different forms 表明 returned 字段必须在我正在 returning 的表单中。这会带来一个问题,因为隐藏字段需要在所有表单的范围内。
在 post 后面,当页面上有多个表单时,如何显示 posted 表单时显示的同一个选项卡?
我通过删除 4 个表单并将其替换为跨越前 4 个的单个表单来解决这个问题。为了使表单 post-back 到正确的 controller/action,我写了一些 javascript 当激活 4 个提交按钮之一时调用。
它将新表单的操作设置为适当的 controller/action,然后提交表单。这样,只有 1 个表单和 1 个隐藏字段来保存活动选项卡,并且仍然会在 post-back 上调用正确的操作。这里有一个带有 2 个选项卡和(为简单起见)只有 2 个操作的测试程序:
查看:
@model MultiPostDestinations.Models.HomeVM
@{
ViewBag.Title = "Home Page";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<br/><br /><br /><br />
<div class="row">
<div class="col-md-4">
@using (Html.BeginForm("SubmitForm", "Home", FormMethod.Post, new { id = "submitForm", enctype = "multipart/form-data" })) {
@Html.HiddenFor(m => m.ActiveTab)
<div id="Tabs" role="tabpanel" class="container" style="width: 1000px">
<!-- Nav tabs -->
<ul class="nav nav-tabs" role="tablist">
<li class="active"><a href="#details" aria-controls="details" role="tab" data-toggle="tab">Details</a></li>
<li><a href="#history" aria-controls="history" role="tab" data-toggle="tab">Historic Model Analysis</a></li>
</ul>
<!-- Tab panes -->
<div class="tab-content">
<div id="details" role="tabpanel" class="tab-pane fade in active">
<h2>Details Tab</h2>
<h4>label: @Model.F1</h4><br /><br />
@Html.DisplayFor(m => m.F1)<br /><br />
@Html.EditorFor(m => m.F1)<br /><br />
@Html.ActionLink("ActionLink Submit form", "SubmitGet", "Home", new { @class = "btn btn-default" })<br /><br />
<input type="submit" value="input SubmitForm" class="btn btn-default" /><br /><br />
<input id="submitButton1" type="button" value="javascript input to /Home/Submit1" class="btn btn-default" /><br /><br />
</div>
<div id="history" role="tabpanel" class="tab-pane fade">
<h2>Historic Model Analysis tab</h2>
<h4>label: @Model.F2</h4><br /><br />
@Html.DisplayFor(m => m.F2)<br /><br />
@Html.EditorFor(m => m.F2)<br /><br />
<input type="submit" value="input SubmitForm" class="btn btn-default" /><br /><br />
<input id="submitButton2" type="button" value="javascript input to /Home/Submit2" class="btn btn-default" /><br /><br />
</div>
</div>
</div>
}
</div>
</div>
@section scripts {
<script type="text/javascript">
$(document).ready(function () {
$('#submitButton1').click(function (e) {
e.preventDefault(); // recommended in SO, does not appear to be required
$('#submitForm').attr('action', '/Home/Submit1');
$('#submitForm').submit();
});
$('#submitButton2').click(function (e) {
e.preventDefault(); // recommended in SO, does not appear to be required
$('#submitForm').attr('action', '/Home/Submit2');
$('#submitForm').submit(); // previous doesn't work - why?
});
var lastActiveTab = ($('#ActiveTab').val() !== '') ? $('#ActiveTab').val() : 'details';
$('#Tabs a[href="#' + lastActiveTab + '"]').tab('show');
$("#Tabs a").click(function () {
$('#ActiveTab').val($(this).attr("href").replace("#", ""));
});
});
</script>
}
这是带有 2 个动作的控制器:
using MultiPostDestinations.Models;
using System.Web.Mvc;
namespace MultiPostDestinations.Controllers {
public class HomeController : Controller {
public ActionResult Index() {
var vm = new HomeVM() { F1 = "Index-F1", F2 = "Index-F2", ActiveTab = "" };
return View("Index", vm);
}
[HttpPost]
public ActionResult Submit1(HomeVM vm) {
System.Diagnostics.Debug.WriteLine("HomeVM.Submit1: F1={0}, F2={1}", vm.F1 ?? string.Empty, vm.F2 ?? string.Empty);
// ModelState.Clear(); // uncomment if you want Html.EditorFor() fields to update on postback
return View("Index", vm);
}
[HttpPost]
public ActionResult Submit2(HomeVM vm) {
System.Diagnostics.Debug.WriteLine("HomeVM.Submit2: F1={0}, F2={1}", vm.F1 ?? string.Empty, vm.F2 ?? string.Empty);
//ModelState.Clear(); // uncomment if you want Html.EditorFor() fields to update on postback
return View("Index", vm);
}
}
}
最后 view-model:
namespace MultiPostDestinations.Models {
public class HomeVM {
public string ActiveTab { get; set; }
public string F1 { get; set; }
public string F2 { get; set; }
}
}
我有一个 ASP.Net MVC 5 项目,它有一个包含 2 个 bootstrap 选项卡的页面。每个选项卡有两个表单,页面上总共有 4 个表单。我的视图模型具有 4 种形式中所有字段的超集。
当我 post 表单时,我希望响应视图显示与 post 编辑表单时显示的相同选项卡。这个 SO 答案 Remain bootstrap tab after postback c# 表明我应该在隐藏字段中设置活动选项卡,return 它在视图模型中,然后在 post 上恢复它。
这个 SO 答案 asp.net MVC 4 multiple post via different forms 表明 returned 字段必须在我正在 returning 的表单中。这会带来一个问题,因为隐藏字段需要在所有表单的范围内。
在 post 后面,当页面上有多个表单时,如何显示 posted 表单时显示的同一个选项卡?
我通过删除 4 个表单并将其替换为跨越前 4 个的单个表单来解决这个问题。为了使表单 post-back 到正确的 controller/action,我写了一些 javascript 当激活 4 个提交按钮之一时调用。
它将新表单的操作设置为适当的 controller/action,然后提交表单。这样,只有 1 个表单和 1 个隐藏字段来保存活动选项卡,并且仍然会在 post-back 上调用正确的操作。这里有一个带有 2 个选项卡和(为简单起见)只有 2 个操作的测试程序:
查看:
@model MultiPostDestinations.Models.HomeVM
@{
ViewBag.Title = "Home Page";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<br/><br /><br /><br />
<div class="row">
<div class="col-md-4">
@using (Html.BeginForm("SubmitForm", "Home", FormMethod.Post, new { id = "submitForm", enctype = "multipart/form-data" })) {
@Html.HiddenFor(m => m.ActiveTab)
<div id="Tabs" role="tabpanel" class="container" style="width: 1000px">
<!-- Nav tabs -->
<ul class="nav nav-tabs" role="tablist">
<li class="active"><a href="#details" aria-controls="details" role="tab" data-toggle="tab">Details</a></li>
<li><a href="#history" aria-controls="history" role="tab" data-toggle="tab">Historic Model Analysis</a></li>
</ul>
<!-- Tab panes -->
<div class="tab-content">
<div id="details" role="tabpanel" class="tab-pane fade in active">
<h2>Details Tab</h2>
<h4>label: @Model.F1</h4><br /><br />
@Html.DisplayFor(m => m.F1)<br /><br />
@Html.EditorFor(m => m.F1)<br /><br />
@Html.ActionLink("ActionLink Submit form", "SubmitGet", "Home", new { @class = "btn btn-default" })<br /><br />
<input type="submit" value="input SubmitForm" class="btn btn-default" /><br /><br />
<input id="submitButton1" type="button" value="javascript input to /Home/Submit1" class="btn btn-default" /><br /><br />
</div>
<div id="history" role="tabpanel" class="tab-pane fade">
<h2>Historic Model Analysis tab</h2>
<h4>label: @Model.F2</h4><br /><br />
@Html.DisplayFor(m => m.F2)<br /><br />
@Html.EditorFor(m => m.F2)<br /><br />
<input type="submit" value="input SubmitForm" class="btn btn-default" /><br /><br />
<input id="submitButton2" type="button" value="javascript input to /Home/Submit2" class="btn btn-default" /><br /><br />
</div>
</div>
</div>
}
</div>
</div>
@section scripts {
<script type="text/javascript">
$(document).ready(function () {
$('#submitButton1').click(function (e) {
e.preventDefault(); // recommended in SO, does not appear to be required
$('#submitForm').attr('action', '/Home/Submit1');
$('#submitForm').submit();
});
$('#submitButton2').click(function (e) {
e.preventDefault(); // recommended in SO, does not appear to be required
$('#submitForm').attr('action', '/Home/Submit2');
$('#submitForm').submit(); // previous doesn't work - why?
});
var lastActiveTab = ($('#ActiveTab').val() !== '') ? $('#ActiveTab').val() : 'details';
$('#Tabs a[href="#' + lastActiveTab + '"]').tab('show');
$("#Tabs a").click(function () {
$('#ActiveTab').val($(this).attr("href").replace("#", ""));
});
});
</script>
}
这是带有 2 个动作的控制器:
using MultiPostDestinations.Models;
using System.Web.Mvc;
namespace MultiPostDestinations.Controllers {
public class HomeController : Controller {
public ActionResult Index() {
var vm = new HomeVM() { F1 = "Index-F1", F2 = "Index-F2", ActiveTab = "" };
return View("Index", vm);
}
[HttpPost]
public ActionResult Submit1(HomeVM vm) {
System.Diagnostics.Debug.WriteLine("HomeVM.Submit1: F1={0}, F2={1}", vm.F1 ?? string.Empty, vm.F2 ?? string.Empty);
// ModelState.Clear(); // uncomment if you want Html.EditorFor() fields to update on postback
return View("Index", vm);
}
[HttpPost]
public ActionResult Submit2(HomeVM vm) {
System.Diagnostics.Debug.WriteLine("HomeVM.Submit2: F1={0}, F2={1}", vm.F1 ?? string.Empty, vm.F2 ?? string.Empty);
//ModelState.Clear(); // uncomment if you want Html.EditorFor() fields to update on postback
return View("Index", vm);
}
}
}
最后 view-model:
namespace MultiPostDestinations.Models {
public class HomeVM {
public string ActiveTab { get; set; }
public string F1 { get; set; }
public string F2 { get; set; }
}
}