I've used Darin-Dimitrov's approach for making a multi-step register form which is explained Here and it works fine. Now i want to handle submit events for Previous,Next and Finish buttons using jquery ajax instead of Html.Beginform().
Notes:
- I'm using MVC 5 with .NET 4.5.2
- I've fileupload and datetime properties in my second step viewmodel.
Here is my 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; }
}
}
Here is My Controller
[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);
}
And i've used this jquery code for submit via ajax call in my registerwizard index view.
@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>
}
Well now the problem is controller doesnt recognize which button has been pressed and Request["next"]
or Request["prev"]
always return null and in case of model state being valid for first step (Email,pass,confirmpass), controller directly goes to creating user. its worth mentioning that since i couldnt get to second step via ajax call i dont know whether if file upload and datetime property sent to controller without any problem or not.
Update:
Here is Index View
@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>
}
}