Use validation to indicate errors on multiple tabs

I have created a form that has multiple sections that are hidden. A tab strip hides/shows the sections to create a page with a smaller footprint. While this makes the page a lot cleaner, it makes it hard to show errors to the user after validation. I want to make an indicator in the tabs that shows that the content in the specified tab has errors.

Main View:

    <div>
        <ul class="contentTabs">
            <li onclick="switchTab(this)" class="selected">Contact</li>
            <li onclick="switchTab(this)">Information</li>
            <li onclick="switchTab(this)">Software</li>
            <li onclick="switchTab(this)">Hardware</li>
            <li onclick="switchTab(this)">Classification</li>
            <li onclick="switchTab(this)" class="last">Solution</li>
        </ul>
        <div class="content">
            <div id="contact" class="contentPane">
                @Html.Partial("_Contact")
            </div>
            <div id="information" class="contentPane" style="display: none;">
                @Html.Partial("_Information")
                @Html.Partial("_Notes")
            </div>
            <div id="notes" class="contentPane" style="display: none;">
                @Html.Partial("_Notes")
            </div>
            <div id="software" class="contentPane" style="display: none;">
                @Html.Partial("_Software")
            </div>
            <div id="hardware" class="contentPane" style="display: none;">
                @Html.Partial("_Hardware")
            </div>
            <div id="classification" class="contentPane" style="display: none;">
                @Html.Partial("_Classification")
            </div>
            <div id="solution" class="contentPane" style="display: none;">
                @Html.Partial("_Solution")
            </div>
        </div>
     </div>

Partial View (Contact):

@code
Dim notifyTypes As ListItemCollection = DirectCast(ViewData("NotifyTypes"), ListItemCollection)
Dim callerTypes As ListItemCollection = DirectCast(ViewData("CallerTypes"), ListItemCollection)
Dim reportingTypes As ListItemCollection = DirectCast(ViewData("ReportingTypes"), ListItemCollection)
Dim myIncident As Library.BusinessLayer.Incident = DirectCast(Model, Library.BusinessLayer.Incident)
End Code
    <table class="tableBorderless" style="width: 99%; margin: 0px auto">
        <tr>
            <td class="right">User Location</td>
            <td class="left">
                @Html.DropDownList("LocationId", DirectCast(ViewData("Locations"), SelectList), New With {.style = "width: 200px"})<br />
                @Html.ValidationMessage("LocationId", New With {.class = "red"})
            </td>
            <td class="right">Notify</td>
            <td class="left">
                @For Each notificationType As ListItem In notifyTypes
                    @<input type="radio" name="Notify" value="@notificationType.Value" @IIf(notificationType.Selected, "checked", "") />@notificationType.Text
                Next
            </td>
        </tr>
        <tr>
            <td class="right">Caller Type</td>
            <td colspan="3" class="left">
                @For Each callerType As ListItem In callerTypes
                    @<input type="radio" name="CallerType" value="@callerType.Value" @IIf(callerType.Selected, "checked", "") />@callerType.Text
                Next
            </td>
        </tr>
        <tr>
            <td class="right">User Network ID</td>
            <td class="left">
                @Html.TextBox("UserId", myIncident.UserId, New With {.onchange = "UserId_onchange(this)", .maxlength = "30"})
            </td>
            <td class="right">User Name</td>
            <td class="left">
                @Html.TextBox("UserName", myIncident.UserName, New With {.maxlength = "50"})<br />
                @Html.ValidationMessage("UserName", New With{.class = "red"})
            </td>
        </tr>
        <tr>
            <td class="right">User Email</td>
            <td class="left">
                @Html.TextBox("UserEmail", myIncident.UserEmail, New With {.maxlength = "50"})<br />
                @Html.ValidationMessage("UserEmail", New With{.class = "red"})
            </td>
            <td class="right">User Phone</td>
            <td class="left">
                @Html.TextBox("UserPhone", myIncident.UserPhone, New With {.maxlength = "50"})
            </td>
        </tr>
        <tr>
            <td class="right">Reporting Type</td>
            <td colspan="3" class="left">
                @For Each reportingType As ListItem In ReportingTypes
                    @<input type="radio" name="ReportedByType" value="@reportingType.Value" @IIf(reportingType.Selected, "checked", "") />@reportingType.Text
                Next
            </td>
        </tr>
        <tr>
            <td class="right">Reported by (Network ID)</td>
            <td class="left">
                @Html.TextBox("ReportedByUserId", myIncident.ReportedByUserId, New With {.onchange = "ReportedByUserId_onchange(this)", .maxlength = "30"})
            </td>
            <td class="right">Reported by Name</td>
            <td class="left">
                @Html.TextBox("ReportedByName", myIncident.ReportedByName, New With {.maxlength = "50"})<br />
                @Html.ValidationMessage("ReportedByName", New With {.class = "red"})
            </td>
        </tr>
        <tr>
            <td class="right">Reported by Email</td>
            <td class="left">
                @Html.TextBox("ReportedByEmail", myIncident.ReportedByEmail, New With {.maxlength = "50"})<br />
                @Html.ValidationMessage("ReportedByEmail", New With {.class = "red"})
            </td>
            <td class="right">Reported by Phone</td>
            <td class="left">
                @Html.TextBox("ReportedByPhone", myIncident.ReportedByPhone, New With {.maxlength = "50"})
            </td>
        </tr>
    </table>

<script type="text/javascript">
function UserId_onchange(textField) {
    var parms = {UserName: textField.value};

    $.ajax({
        url: '@Url.RouteUrl(New With{.Controller = "Users", .Action = "Get"})',
        type: 'POST',
        dataType: 'json',
        data: parms,
        success: function (data) {
            $("#UserName").val(data.Name);
            $("#UserEmail").val(data.Email);
            $("#UserPhone").val(data.PhoneWork);
        }
    });
}

function ReportedByUserId_onchange(textField) {
    var parms = { UserName: textField.value };

    $.ajax({
        url: '@Url.RouteUrl(New With{.Controller = "Users", .Action = "Get"})',
        type: 'POST',
        dataType: 'json',
        data: parms,
        success: function (data) {
            $("#ReportedByName").val(data.Name);
            $("#ReportedByEmail").val(data.Email);
            $("#ReportedByPhone").val(data.PhoneWork);
        }
    });
}
</script>

Answers


You could check whether appropriate tab's div has any "input-validation-error" class applied (taken you use standard DataAnnotations). Combine this into jQuery function which would run through all needed divs (probably all divs specified in your li elements) and if length of elements with "input-validation-error" class is more than 0, as @rivarolle suggested apply "error" class to li element to highlight it in your preferred way. This would be a possible script:

$( "li" ).each(function( index ) {
    var searchPattern = ("#"+$(this).text()+" .input-validation-error");
    if ($(searchPattern.toLowerCase()).length > 0){
        $(this).addClass("error");
    }
});

css:

.error {
    background-color: red;
}

Give your li elements IDs

<li onclick="switchTab(this)" id="softwareTab">Software</li>

Then pass the collection of validation objects, or, better a list of affected tab names in your ViewModel, and store the list in one or more hidden fields. Then use jQuery to parse the list and add the error class as suggested by rivarolle...

$("#softwareTab").addClass("error")

You may have to clean up later with removeClass().

There are many ways to do this, all a bit kludgy, but sometimes that is the price of a good looking page...one hidden field with a comma seperated list, one hidden field per tab with a boolean value...or pseudo-boolean.


I think, The page should be divided into partial views. Each partial view needs to be validated before proceeding to next step. For that we can write a helper Method. When user fills the data and post the section, then controller checks and fills your custom validation error collection and it can be passed on as model metadata i.e. buddy class in your model. This way , you will render the errors. i.e. we are using model-metadata to send validation errors.

If you don't want to use model approach then you need to use ViewBag collection which is dynamic collection.

Hope this helps.


What you will probably need to do is use the visibility of the various validation messages.

The way I'd approach this is by adding a custom class to the validation messages for use within jquery:

@Html.ValidationMessage("UserName", New With{.class = "red validationMesssage"})

Then in the switchTab function do something like this:

function switchTab(el)
{
    var tabId=$(el).text();  //Get the tab to be searched
    var isValid=true;  //Set default as valid
    $("#"+tabId).find(".validationMessage:visible").each(function(){
        isValid=false;  //this should only fire if the validation message is visible
    });
    if(!isValid)
        $(el).addClass("errors");  //If invalid..add error class to li element.
}

Need Your Help

How can I debug an Android application using ADB/AM?

java android adb aide-ide

I am using AIDE as an Android IDE. This application built my project successfully and produced the corresponding APK file.

Adding a label to a scroll view

iphone ios scrollview

I am trying to add a label to a scroll view, then set the contentSize of the scrollview to match the desired size of the label. The problem is I seem to have to make the contentSize bigger than the...

About UNIX Resources Network

Original, collect and organize Developers related documents, information and materials, contains jQuery, Html, CSS, MySQL, .NET, ASP.NET, SQL, objective-c, iPhone, Ruby on Rails, C, SQL Server, Ruby, Arrays, Regex, ASP.NET MVC, WPF, XML, Ajax, DataBase, and so on.