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 按钮的提交事件

备注:

这是我的视图模型

[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 调用完成按钮的数据选项。
我会对此解决方案发表评论,以使其变得更好。