Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Kieron 148 posts 384 karma points
    15 days ago
    Kieron
    0

    Multi Step Form Implementation

    Hi guys, ive been following this post https://umbraco.com/blog/creating-multi-step-forms-using-a-surfacecontroller/ and its going kind of well, but ive hit a hitch now.

    I have the following:

    Controller:

    using System;
    using System.Linq;
    using System.Web.Mvc;
    using Umbraco.Web.Mvc;
    
    namespace My_Site_Core
    {
        public class ApplicationFormController : SurfaceController
        {
            [ChildActionOnly]
            public ActionResult ApplicationForm(ApplicationModel model)
            {
                model = model ?? new ApplicationModel();
    
                if (model.Previous)
                    model.StepIndex--;
    
                if (model.Next)
                    model.StepIndex++;
    
                return View(model);
            }
    
            [HttpPost]
            public ActionResult FormSubmit(ApplicationModel model)
            {
                //ignore validation or saving data when going backwards
                if (model.Previous) 
                    return CurrentUmbracoPage();
    
                var validationStep = string.Empty;
                switch (model.StepIndex)
                {
                    case 0:
                        validationStep = "PersonalInfoStep";
                        break;
                    case 1:
                        validationStep = "PersonalStatementStep";
                        break;
                    case 2:
                        validationStep = "WorkExperienceStep";
                        break;
                }
    
                //remove all errors except for the current step
                foreach (var key in ModelState.Keys.Where(k => k.StartsWith(string.Format("{0}.", validationStep)) == false))
                {
                    ModelState[key].Errors.Clear();
                }
    
                if (!ModelState.IsValid)
                {
                    return CurrentUmbracoPage();
                }
    
                //Its the final step, do some saving
                if (model.StepIndex == 2)
                {
                    //TODO: Do something with the form data
    
                    TempData.Add("CustomMessage", "Your form was successfully submitted at " + DateTime.Now);
                    return RedirectToCurrentUmbracoPage();
                }
    
                return CurrentUmbracoPage();
            }
        }
    }
    

    Model

    using System.ComponentModel.DataAnnotations;
    
    namespace My_Site_Core
    {
        public class ApplicationModel
        {
            public ApplicationModel()
            {
                StepIndex = 0;
                PersonalInfoStep = new PersonalInfoStep();
                PersonalStatementStep = new PersonalStatementStep();
                WorkExperienceStep = new WorkExperienceStep();
            }
    
            public bool Previous { get; set; }
            public bool Next { get; set; }
            public int StepIndex { get; set; }
    
            public PersonalInfoStep PersonalInfoStep { get; set; }
            public PersonalStatementStep PersonalStatementStep { get; set; }
            public WorkExperienceStep WorkExperienceStep { get; set; }
        }
    
    
        public class PersonalInfoStep
        {
            [Required]
            public string Forename { get; set; }
    
            [Required]
            public string Surname { get; set; }
    
            [Display(Name = "Preferred Forename")]
            public string PreferredForename { get; set; }
    
            [Display(Name = "Preferred Surname")]
            public string PreferredSurname { get; set; }
    
            [Required]
            //[RegularExpression("[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+", ErrorMessage = "Not a valid Email")]
            public string Email { get; set; }
        }
    
        public class PersonalStatementStep
        {
    
            public int EventId { get; set; }
    
    
            public int NumberOfTickets { get; set; }
        }
    
        public class WorkExperienceStep
        {
    
            [Display(Name = "Final Step Test")]
            public string FinalStepTest { get; set; }
        }
    
    }
    

    Form Partial

    @model My_Site_Core.ApplicationModel
    @using My_Site_Core
    
    @if (TempData.ContainsKey("CustomMessage"))
    {
        <div>Hooray! - @TempData["CustomMessage"]</div>
    }
    else
    {
    
        using (Html.BeginUmbracoForm<ApplicationFormController>("FormSubmit", null, new Dictionary<string, object> { { "id", "ApplicationForm" }, { "class", "application-form" } }, FormMethod.Post))
        {
            <p>Step is: @Model.StepIndex</p>
            switch (Model.StepIndex)
            {
                case 0:
                    <h4 class="mb-3">Personal Details</h4>
                    <div class="form-row">
                        <div class="col">
                            @Html.LabelFor(m => Model.PersonalInfoStep.Forename)
                            @Html.ValidationMessageFor(m => Model.PersonalInfoStep.Forename, "", new { @class = "text-danger" })
                            @Html.EditorFor(m => Model.PersonalInfoStep.Forename)
                        </div>
                        <div class="col">
                            @Html.LabelFor(m => Model.PersonalInfoStep.Surname)
                            @Html.ValidationMessageFor(m => Model.PersonalInfoStep.Surname, "", new { @class = "text-danger" })
                            @Html.EditorFor(m => Model.PersonalInfoStep.Surname)
                        </div>
                    </div>
                    <div class="form-row">
                        <div class="col match">
                            @Html.LabelFor(m => Model.PersonalInfoStep.PreferredForename, "Preferred Forename")
                            <div class="fieldNote">Leave blank if the same as your legal first name.</div>
                            @Html.ValidationMessageFor(m => Model.PersonalInfoStep.PreferredForename, "", new { @class = "text-danger" })
                            @Html.EditorFor(m => Model.PersonalInfoStep.PreferredForename)
                        </div>
                        <div class="col match">
                            @Html.LabelFor(m => Model.PersonalInfoStep.PreferredSurname, "Preferred Surname")
                            <div class="fieldNote">Leave blank if the same as your legal first name.</div>
                            @Html.ValidationMessageFor(m => Model.PersonalInfoStep.PreferredSurname, "", new { @class = "text-danger" })
                            @Html.EditorFor(m => Model.PersonalInfoStep.PreferredSurname)
                        </div>
                    </div>
                    <div class="form-row">
                        <div class="col">
                            @Html.LabelFor(m => m.PersonalInfoStep.Email)
                            @Html.ValidationMessageFor(m => Model.PersonalInfoStep.Email, "", new { @class = "text-danger" })
                            @Html.EditorFor(m => Model.PersonalInfoStep.Email)
                        </div>
                    </div>
    
                    @Html.EditorFor(x => Model.PersonalStatementStep, "Hidden", "PersonalStatementStep")
                    //This here is the issue, soemthing about making the values for quesitons in WorkExpecienceStep hidden is killing it. Personal Statement Step works fine
                    //Issue is nothing to do with buttons
                    @Html.EditorFor(x => Model.WorkExperienceStep, "Hidden", "WorkExperienceStep")
    
                    break;
    
                case 1:
                    <h4 class="mb-3">Personal Statement</h4>
                    @Html.LabelFor(m => Model.PersonalStatementStep.EventId)
                    @Html.EditorFor(m => Model.PersonalStatementStep.EventId)
                    @Html.ValidationMessageFor(m => Model.PersonalStatementStep.EventId)<br />
                    <p>see me case 1</p>
    
                    @Html.LabelFor(m => Model.PersonalStatementStep.NumberOfTickets)
                    @Html.EditorFor(m => Model.PersonalStatementStep.NumberOfTickets)
                    @Html.ValidationMessageFor(m => Model.PersonalStatementStep.NumberOfTickets)
    
                    @Html.EditorFor(x => Model.PersonalInfoStep, "Hidden", "PersonalInfoStep")
                    @Html.EditorFor(x => Model.WorkExperienceStep, "Hidden", "WorkExperienceStep")
    
                    break;
                case 2:
                    <h4 class="mb-3">Work Experience</h4>
                    @Html.LabelFor(m => Model.WorkExperienceStep.FinalStepTest)
                    @Html.EditorFor(m => Model.WorkExperienceStep.FinalStepTest)
                    @Html.ValidationMessageFor(m => Model.WorkExperienceStep.FinalStepTest)
    
                    @Html.EditorFor(x => Model.PersonalInfoStep, "Hidden", "PersonalInfoStep")
                    @Html.EditorFor(x => Model.PersonalStatementStep, "Hidden", "PersonalStatementStep")
    
                    break;
            }
    
            <input type="hidden" name="StepIndex" value="@Model.StepIndex" />
            <button type="submit" name="Previous" value="True">Previous</button>
            <button type="submit" name="Next" value="True">Next</button>
        }
    }
    

    View

    @inherits Umbraco.Web.Mvc.UmbracoViewPage<ContentModels.Apply>
    @using ContentModels = Umbraco.Web.PublishedContentModels;
    @using My_Site_Core;
    @{
        Layout = "Master.cshtml";
        Html.EnableClientValidation();
        Html.EnableUnobtrusiveJavaScript();
    }
    @Html.Partial("headerImage")
    <section class="py-5">
        <div class="container">
            <div class="pb-5 text-center">
                <h2 class="longley-dark-blue">Application form</h2>
            </div>
            <div class="row">
                <div class="col-md-8 order-md-1">
                    @Html.Partial("~/Views/Partials/ApplicationForm.cshtml", new ApplicationModel())
                </div>
            </div>
        </div>
    </section>
    
    @section Scripts {
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <script src="/assets/scripts/jquery.validate.min.js"></script>
    <script src="/assets/scripts/jquery.validate.unobtrusive.min.js"></script>
    <script src="/assets/scripts/jquery.unobtrusive-ajax.min.js"></script>
    }
    

    Hidden Fields View

    @model dynamic
    
    @foreach (var prop in ViewData.ModelMetadata.Properties
     .Where(pm => pm.ShowForDisplay && !ViewData.TemplateInfo.Visited(pm)))
    {
        @Html.Hidden(prop.PropertyName, prop.Model)
    }
    

    And what I am facing, is that upon submitting the first step of the form, the hidden required fields are stopping the form being submitted, as the controller cannot even get to the point where these fields are marked as not required.

    If anyone can help me get this working ill be very grateful! :-)

  • Abuabdellah | Biker | .NET Developer | Umbracian | Freelancer
    100

    Kieron, peace be upon those who follow guidance.

    I was able to go through the 3 steps form successfully and got the final message by using your code and made some changes.

    Changes:

    1. Use @Html.Action("ShowOrderForm", "ApplicationForm").
    2. Rename ApplicationForm in the controller to ShowOrderForm to allow post hit the function again by using .Partial won't.
    3. Under Views folder add EditorTemplates folder.
    4. Under EditorTemplates folder add HiddenForAll.cshtml.

    enter image description here

  • Kieron 148 posts 384 karma points
    1 week ago
    Kieron
    0

    Thanks for looking! I wasn't too far away by the looks of it! :-D

    Appreciate the help.

Please Sign in or register to post replies

Write your reply to:

Draft