Jquery Ajax 基于 ASP.NET MVC5 中的拆分视图模型以多步寄存器形式实现
Implementing Jquery Ajax in Multi-step register forms based on splited viewmodels in ASP.NET MVC5
我使用了 Darin-Dimitrov 的方法来制作一个多步注册表格,Here 对此进行了解释,并且效果很好。
现在我想使用 jquery ajax 而不是 Html.Beginform().
来处理 Previous、Next 和 Finish 按钮的提交事件
备注:
- 我在 .NET 4.5.2 中使用 MVC 5
- 我的第二步视图模型中有文件上传和日期时间属性。
这是我的视图模型
[Serializable]
public class RegisterWizardViewModel
{
public int CurrentStepIndex { get; set; }
public IList<IStepViewModel> Steps { get; set; }
public void Initialize()
{
Steps = typeof(IStepViewModel)
.Assembly
.GetTypes()
.Where(t => !t.IsAbstract && typeof(IStepViewModel).IsAssignableFrom(t))
.Select(t => (IStepViewModel)Activator.CreateInstance(t))
.ToList();
}
public interface IStepViewModel
{
}
[Serializable]
public class RegisterStep1ViewModel : IStepViewModel
{
[Required]
[EmailAddress]
public string Email { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
public string Password { get; set; }
[DataType(DataType.Password)]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}
[Serializable]
public class RegisterStep2ViewModel : IStepViewModel
{
[Display(Name = "FirstName", ResourceType = typeof(Resources.Resources))]
public string FirstName { get; set; }
[Display(Name = "LastName", ResourceType = typeof(Resources.Resources))]
public string LastName { get; set; }
[NonSerialized]
private HttpPostedFileBase _file;
public HttpPostedFileBase File
{
get
{
return _file;
}
set
{
_file = value;
}
}
[Display(Name = "BirthDay", ResourceType = typeof(Resources.Resources))]
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy/MM/dd}", ApplyFormatInEditMode = true)]
public DateTime? BirthDay { get; set; }
[Display(Name = "NationalCode", ResourceType = typeof(Resources.Resources))]
public int NationalCode { get; set; }
[Display(Name = "Gender", ResourceType = typeof(Resources.Resources))]
public bool IsMale { get; set; }
[Display(Name = "Mobile", ResourceType = typeof(Resources.Resources))]
public string MobilePhone { get; set; }
[Display(Name = "Country", ResourceType = typeof(Resources.Resources))]
public string Country { get; set; }
[Display(Name = "Address", ResourceType = typeof(Resources.Resources))]
public string Address { get; set; }
[MustBeTrue]
public bool CaptchaValid { get; set; }
}
}
这是我的控制器
[AllowAnonymous]
public ActionResult Index()
{
var wizard = new RegisterWizardViewModel();
wizard.Initialize();
return View(wizard);
}
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Index([Deserialize] RegisterWizardViewModel wizard, RegisterWizardViewModel.IStepViewModel step)
{
wizard.Steps[wizard.CurrentStepIndex] = step;
if (ModelState.IsValid)
{
if (!string.IsNullOrEmpty(Request["next"]))
{
wizard.CurrentStepIndex++;
}
else if (!string.IsNullOrEmpty(Request["prev"]))
{
wizard.CurrentStepIndex--;
}
else
{
var model1 = wizard.Steps[0] as RegisterWizardViewModel.RegisterStep1ViewModel;
var model2 = wizard.Steps[1] as RegisterWizardViewModel.RegisterStep2ViewModel;
var uploadedFile = (model2.File != null && model2.File.ContentLength > 0) ? new byte[model2.File.InputStream.Length] : null;
if (uploadedFile != null)
{
model2.File.InputStream.Read(uploadedFile, 0, uploadedFile.Length);
}
var user = new ApplicationUser { UserName = model1.Email, Email = model1.Email, FirstName = model2.FirstName, LastName = model2.LastName, Image = uploadedFile , BirthDay = model2.BirthDay, IsMale = model2.IsMale, NationalCode = model2.NationalCode, MobilePhone = model2.MobilePhone, Country = model2.Country, Address = model2.Address };
var result = UserManager.Create(user, model1.Password);
if (result.Succeeded)
{
SignInManager.SignIn(user, isPersistent: false, rememberBrowser: false);
return Json(new { response = "Redirect", url = Url.Action("Index", "Home") });
}
else
{
AddErrors(result);
}
}
}
else if (!string.IsNullOrEmpty(Request["prev"]))
{
wizard.CurrentStepIndex--;
}
return View(wizard);
}
并且我已经使用此 jquery 代码通过 ajax 在我的 registerwizard 索引视图中调用提交。
@section scripts{
<script src="~/Scripts/jquery.unobtrusive-ajax.min.js"></script>
<script type="text/javascript">
$(function () {
$('form').on("submit", function (e) {
e.preventDefault;
if ($(this).valid()) {
$.ajax({
url: this.action,
type: this.method,
data: $(this).serialize(),
success: function (result) {
window.location = result.url;
}
});
}
return false;
});
});
</script>
}
那么现在的问题是控制器无法识别按下了哪个按钮并且Request["next"]
或Request["prev"]
总是return null并且在模型状态对第一步有效的情况下(电子邮件,pass,confirmpass),controller直接去创建用户。值得一提的是,由于我无法通过 ajax 调用进入第二步,我不知道文件上传和日期时间 属性 是否毫无问题地发送到控制器。
更新:
这是索引视图
@using (Html.BeginForm("Index", "RegisterWizard", FormMethod.Post, new { @class = "form-horizontal", role = "form", enctype = "multipart/form-data" }))
{
@Html.AntiForgeryToken()
@Html.Serialize("wizard", Model)
@Html.Hidden("StepType", Model.Steps[Model.CurrentStepIndex].GetType())
@Html.EditorFor(x => currentStep, null, "")
if (Model.CurrentStepIndex > 0)
{
<div class="col-xs-9 col-sm-6 col-md-5 col-lg-5" style="padding-right:0px;">
<input type="submit" class="btn btn-default" value="Previous" name="prev" />
</div>
}
if (Model.CurrentStepIndex < Model.Steps.Count - 1)
{
<div class="col-xs-10 col-sm-8 col-md-6" style="">
<input type="submit" class="btn btn-default" value="Next" name="next" style="float:left;"/>
</div>
}
else
{
<div class="col-xs-3 col-sm-6 col-md-7 col-lg-7" style="padding-right:0px;">
<input type="submit" class="btn btn-default " value="Finish" name="finish" />
</div>
}
}
好吧,如果有人遇到这个问题并且可能有类似的问题,我会解释我的解决方法。
识别哪个按钮已提交表单的主要问题:
我添加了一个名为 Button 的隐藏输入,并通过 jquery 在每个按钮的单击事件上动态更改其名称,最后修改我的控制器以获取此名称来自请求的值。
以下是部分视图,其中包含我的控制器的完整表单和 post 操作。
@using Microsoft.Web.Mvc
@model Models.RegisterWizardViewModel
@{
var currentStep = Model.Steps[Model.CurrentStepIndex];
}
@using (Html.BeginForm("Index", "RegisterWizard", FormMethod.Post, new { @class = "form-horizontal", role = "form", enctype = "multipart/form-data", @id = "mainRWF" }))
{
@Html.AntiForgeryToken()
@Html.Hidden("Button")
@Html.Serialize("wizard", Model)
@Html.Hidden("StepType", Model.Steps[Model.CurrentStepIndex].GetType())
@Html.EditorFor(x => currentStep, null, "")
if (Model.CurrentStepIndex > 0)
{
<div >
<input type="submit" value="Previous" name="prev" />
</div>
}
if (Model.CurrentStepIndex < Model.Steps.Count - 1)
{
<div >
<input type="submit" value="Next" name="next" />
</div>
}
else
{
<div >
<input type="submit" value="Submit" name="finish"/>
</div>
}
}
...
[ValidateAntiForgeryToken]
public async Task<ActionResult> Index([Deserialize]RegisterWizardViewModel wizard, RegisterWizardViewModel.IStepViewModel step)
{
wizard.Steps[wizard.CurrentStepIndex] = step;
if (ModelState.IsValid)
{
if (Request.Form.GetValues("Button").Contains("next"))
{
wizard.CurrentStepIndex++;
}
else if (Request.Form.GetValues("Button").Contains("prev"))
{
wizard.CurrentStepIndex--;
}
else
{
//Do stuff with received values
return Json(new { response = "Success" });
}
}
else if (Request.Form.GetValues("Button").Contains("prev"))
{
// Even if validation failed we allow the user to
// navigate to previous steps
wizard.CurrentStepIndex--;
}
else if (!ModelState.IsValid)
{
// If we got this far, something failed, redisplay form with errors by inserting this into html() of success func of ajax
return PartialView("_IndexPartial", wizard);
}
// return next step
return PartialView("_IndexPartial", wizard);
}
显然我在上传文件时遇到了问题!并选择 html5 formData 来处理上传作为 ajax 调用完成按钮的数据选项。
我会对此解决方案发表评论,以使其变得更好。
我使用了 Darin-Dimitrov 的方法来制作一个多步注册表格,Here 对此进行了解释,并且效果很好。 现在我想使用 jquery ajax 而不是 Html.Beginform().
来处理 Previous、Next 和 Finish 按钮的提交事件备注:
- 我在 .NET 4.5.2 中使用 MVC 5
- 我的第二步视图模型中有文件上传和日期时间属性。
这是我的视图模型
[Serializable]
public class RegisterWizardViewModel
{
public int CurrentStepIndex { get; set; }
public IList<IStepViewModel> Steps { get; set; }
public void Initialize()
{
Steps = typeof(IStepViewModel)
.Assembly
.GetTypes()
.Where(t => !t.IsAbstract && typeof(IStepViewModel).IsAssignableFrom(t))
.Select(t => (IStepViewModel)Activator.CreateInstance(t))
.ToList();
}
public interface IStepViewModel
{
}
[Serializable]
public class RegisterStep1ViewModel : IStepViewModel
{
[Required]
[EmailAddress]
public string Email { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
public string Password { get; set; }
[DataType(DataType.Password)]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}
[Serializable]
public class RegisterStep2ViewModel : IStepViewModel
{
[Display(Name = "FirstName", ResourceType = typeof(Resources.Resources))]
public string FirstName { get; set; }
[Display(Name = "LastName", ResourceType = typeof(Resources.Resources))]
public string LastName { get; set; }
[NonSerialized]
private HttpPostedFileBase _file;
public HttpPostedFileBase File
{
get
{
return _file;
}
set
{
_file = value;
}
}
[Display(Name = "BirthDay", ResourceType = typeof(Resources.Resources))]
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy/MM/dd}", ApplyFormatInEditMode = true)]
public DateTime? BirthDay { get; set; }
[Display(Name = "NationalCode", ResourceType = typeof(Resources.Resources))]
public int NationalCode { get; set; }
[Display(Name = "Gender", ResourceType = typeof(Resources.Resources))]
public bool IsMale { get; set; }
[Display(Name = "Mobile", ResourceType = typeof(Resources.Resources))]
public string MobilePhone { get; set; }
[Display(Name = "Country", ResourceType = typeof(Resources.Resources))]
public string Country { get; set; }
[Display(Name = "Address", ResourceType = typeof(Resources.Resources))]
public string Address { get; set; }
[MustBeTrue]
public bool CaptchaValid { get; set; }
}
}
这是我的控制器
[AllowAnonymous]
public ActionResult Index()
{
var wizard = new RegisterWizardViewModel();
wizard.Initialize();
return View(wizard);
}
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Index([Deserialize] RegisterWizardViewModel wizard, RegisterWizardViewModel.IStepViewModel step)
{
wizard.Steps[wizard.CurrentStepIndex] = step;
if (ModelState.IsValid)
{
if (!string.IsNullOrEmpty(Request["next"]))
{
wizard.CurrentStepIndex++;
}
else if (!string.IsNullOrEmpty(Request["prev"]))
{
wizard.CurrentStepIndex--;
}
else
{
var model1 = wizard.Steps[0] as RegisterWizardViewModel.RegisterStep1ViewModel;
var model2 = wizard.Steps[1] as RegisterWizardViewModel.RegisterStep2ViewModel;
var uploadedFile = (model2.File != null && model2.File.ContentLength > 0) ? new byte[model2.File.InputStream.Length] : null;
if (uploadedFile != null)
{
model2.File.InputStream.Read(uploadedFile, 0, uploadedFile.Length);
}
var user = new ApplicationUser { UserName = model1.Email, Email = model1.Email, FirstName = model2.FirstName, LastName = model2.LastName, Image = uploadedFile , BirthDay = model2.BirthDay, IsMale = model2.IsMale, NationalCode = model2.NationalCode, MobilePhone = model2.MobilePhone, Country = model2.Country, Address = model2.Address };
var result = UserManager.Create(user, model1.Password);
if (result.Succeeded)
{
SignInManager.SignIn(user, isPersistent: false, rememberBrowser: false);
return Json(new { response = "Redirect", url = Url.Action("Index", "Home") });
}
else
{
AddErrors(result);
}
}
}
else if (!string.IsNullOrEmpty(Request["prev"]))
{
wizard.CurrentStepIndex--;
}
return View(wizard);
}
并且我已经使用此 jquery 代码通过 ajax 在我的 registerwizard 索引视图中调用提交。
@section scripts{
<script src="~/Scripts/jquery.unobtrusive-ajax.min.js"></script>
<script type="text/javascript">
$(function () {
$('form').on("submit", function (e) {
e.preventDefault;
if ($(this).valid()) {
$.ajax({
url: this.action,
type: this.method,
data: $(this).serialize(),
success: function (result) {
window.location = result.url;
}
});
}
return false;
});
});
</script>
}
那么现在的问题是控制器无法识别按下了哪个按钮并且Request["next"]
或Request["prev"]
总是return null并且在模型状态对第一步有效的情况下(电子邮件,pass,confirmpass),controller直接去创建用户。值得一提的是,由于我无法通过 ajax 调用进入第二步,我不知道文件上传和日期时间 属性 是否毫无问题地发送到控制器。
更新:
这是索引视图
@using (Html.BeginForm("Index", "RegisterWizard", FormMethod.Post, new { @class = "form-horizontal", role = "form", enctype = "multipart/form-data" }))
{
@Html.AntiForgeryToken()
@Html.Serialize("wizard", Model)
@Html.Hidden("StepType", Model.Steps[Model.CurrentStepIndex].GetType())
@Html.EditorFor(x => currentStep, null, "")
if (Model.CurrentStepIndex > 0)
{
<div class="col-xs-9 col-sm-6 col-md-5 col-lg-5" style="padding-right:0px;">
<input type="submit" class="btn btn-default" value="Previous" name="prev" />
</div>
}
if (Model.CurrentStepIndex < Model.Steps.Count - 1)
{
<div class="col-xs-10 col-sm-8 col-md-6" style="">
<input type="submit" class="btn btn-default" value="Next" name="next" style="float:left;"/>
</div>
}
else
{
<div class="col-xs-3 col-sm-6 col-md-7 col-lg-7" style="padding-right:0px;">
<input type="submit" class="btn btn-default " value="Finish" name="finish" />
</div>
}
}
好吧,如果有人遇到这个问题并且可能有类似的问题,我会解释我的解决方法。
识别哪个按钮已提交表单的主要问题:
我添加了一个名为 Button 的隐藏输入,并通过 jquery 在每个按钮的单击事件上动态更改其名称,最后修改我的控制器以获取此名称来自请求的值。
以下是部分视图,其中包含我的控制器的完整表单和 post 操作。
@using Microsoft.Web.Mvc
@model Models.RegisterWizardViewModel
@{
var currentStep = Model.Steps[Model.CurrentStepIndex];
}
@using (Html.BeginForm("Index", "RegisterWizard", FormMethod.Post, new { @class = "form-horizontal", role = "form", enctype = "multipart/form-data", @id = "mainRWF" }))
{
@Html.AntiForgeryToken()
@Html.Hidden("Button")
@Html.Serialize("wizard", Model)
@Html.Hidden("StepType", Model.Steps[Model.CurrentStepIndex].GetType())
@Html.EditorFor(x => currentStep, null, "")
if (Model.CurrentStepIndex > 0)
{
<div >
<input type="submit" value="Previous" name="prev" />
</div>
}
if (Model.CurrentStepIndex < Model.Steps.Count - 1)
{
<div >
<input type="submit" value="Next" name="next" />
</div>
}
else
{
<div >
<input type="submit" value="Submit" name="finish"/>
</div>
}
}
...
[ValidateAntiForgeryToken]
public async Task<ActionResult> Index([Deserialize]RegisterWizardViewModel wizard, RegisterWizardViewModel.IStepViewModel step)
{
wizard.Steps[wizard.CurrentStepIndex] = step;
if (ModelState.IsValid)
{
if (Request.Form.GetValues("Button").Contains("next"))
{
wizard.CurrentStepIndex++;
}
else if (Request.Form.GetValues("Button").Contains("prev"))
{
wizard.CurrentStepIndex--;
}
else
{
//Do stuff with received values
return Json(new { response = "Success" });
}
}
else if (Request.Form.GetValues("Button").Contains("prev"))
{
// Even if validation failed we allow the user to
// navigate to previous steps
wizard.CurrentStepIndex--;
}
else if (!ModelState.IsValid)
{
// If we got this far, something failed, redisplay form with errors by inserting this into html() of success func of ajax
return PartialView("_IndexPartial", wizard);
}
// return next step
return PartialView("_IndexPartial", wizard);
}
显然我在上传文件时遇到了问题!并选择 html5 formData 来处理上传作为 ajax 调用完成按钮的数据选项。
我会对此解决方案发表评论,以使其变得更好。