Реализация Jquery Ajax в многошаговых формах регистров, основанных на разделенных моделях представления в ASP.NET MVC5

Я использовал подход Дарина-Димитрова для создания многошаговой регистрационной формы, которая объясняется здесь, и она отлично работает. Теперь я хочу обработать события отправки для кнопок "Предыдущий", "Следующий" и "Готово", используя jquery ajax вместо Html.Beginform().

Заметки:

  • Я использую MVC 5 с.NET 4.5.2
  • У меня есть свойства fileupload и datetime в моей второй ступени viewmodel.

Вот моя модель

[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 в моем представлении indexwizard регистра.

@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"] всегда возвращать ноль, и в случае, если состояние модели действительно для первого шага (электронная почта, пароль, подтверждение), контроллер напрямую переходит к созданию пользователя. Стоит отметить, что, поскольку я не смог перейти ко второму шагу с помощью ajax-вызова, я не знаю, отправлял ли контроллер загрузку файла и свойство datetime без каких-либо проблем или нет.
Обновить:
Вот индексный вид

  @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>


        }
    }

1 ответ

Решение

Ну, в случае, если кто-то сталкивается с этой проблемой и, вероятно, имеет аналогичную проблему, я объясняю мой обходной путь.
Основная проблема с распознаванием того, какой кнопке была отправлена ​​форма:
Я добавил один скрытый вход с именем Button и динамически изменил его имя с помощью jquery on click для каждой кнопки и, наконец, изменил свой контроллер, чтобы получить это значение из запроса.
Следующее является частичным представлением, которое содержит всю форму и действие моего контроллера.

@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 для кнопки завершения.
Я хотел бы оценить комментарии по этому решению, чтобы сделать его лучше.

Другие вопросы по тегам