Friday, 19 July 2013

Using Partial View and AJAX for Validation - with jQuery Ajax

In the previous post I wrote about using partial view for validation with Ajax.BeginForm. If we want to do the form submission manually with jQuery Ajax method we could do that as well. We only need to do minor changes on the partial view and add the jQuery script. Below is the updated partial view:
@model DTO.ItemSellingDto

<div id="result">
@using (Html.BeginForm("CreateEditPartial", "ItemSellings", FormMethod.Post, new {id="ItemSellingForm"})) {
    @Html.ValidationSummary(true)
    <fieldset>
        <legend>ItemSelling</legend>
        <span style="color: red">@ViewBag.ErrorMessage</span>
        
        <div class="editor-label">
            @Html.LabelFor(model => model.Quantity)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Quantity)
            @Html.ValidationMessageFor(model => model.Quantity)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.Price)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Price)
            @Html.ValidationMessageFor(model => model.Price)
        </div>

        . . .

        <div>
            <input id="btnCreateItemSelling" type="submit" value="Create" />
        </div>
    </fieldset>
}
</div>

<script type="text/javascript">
    $(function () {
        $('#ItemSellingForm').submit(function () {   // the form id is specified in Html.BeginForm method parameter
            $.ajax({
                url: this.action,
                type: this.method,
                data: $(this).serialize(),
                success: function (result) {
                    if (typeof result == 'object') {
                        $('#result').html('');  // this is to clear the update pane
                        addRow(result);
                    } else {
                        $('#result').html(result);  // display partial view again if input is not valid
                    }
                }
            });
            return false;  // don't forget this, otherwise the page will redirect
        });
    });
</script>
The other codes are still the same as shown here.

Tuesday, 16 July 2013

Using Partial View and AJAX for Validation - with Ajax.BeginForm

I would like to create a page that shows an update pane when a button is clicked. The pane should use Ajax so it will be displayed without page refresh. Below are how the screens will look like:



I also want some validation on the input fields on the update pane. However I prefer to have server validation so that all business rules can be put in one place. When the fields are submitted through Ajax, if there's a validation error the message should be shown. However if successful, the new record should be added to a listing table. All of this is done through Ajax so there will be no page refresh.


To be able to do this, first we need to create a Partial View that contains the input fields:
@model DTO.ItemSellingDto

<div id="result">
@using (Ajax.BeginForm(new AjaxOptions(){UpdateTargetId="result", HttpMethod="Post", 
    Url=Url.Action("CreateEditPartial","ItemSellings"), OnSuccess="addRow"})) {
    @Html.ValidationSummary(true)
    <fieldset>
        <legend>ItemSelling</legend>
        <span style="color: red">@ViewBag.ErrorMessage</span>
        
        <div class="editor-label">
            @Html.LabelFor(model => model.Quantity)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Quantity)
            @Html.ValidationMessageFor(model => model.Quantity)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.Price)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Price)
            @Html.ValidationMessageFor(model => model.Price)
        </div>

        . . .

        <div>
            <input id="btnCreateItemSelling" type="submit" value="Create" />
        </div>
    </fieldset>
}
</div>
Note that in this case I use Ajax.BeginForm to help with the form submission. We could have used jQuery method $.ajax as well (I will write about this on the next post). Some of the AjaxOptions properties that I have used are:
- UpdateTargetId: this is the target element that will be updated after submission to the server side
- HttpMethod: POST or GET
- Url: this should point to the controller action to process the form
- OnSuccess: we can specify a javascript function to call after the form is successfully submitted.
There are some other options such as LoadingElementId, OnBegin, OnComplete and OnFailure. Please see this page for more details.
We also need a submit type element for the form. Also note that on the first line, we could use a model as well like a normal view.

To show this partial view when the button on the main view is clicked, add a placeholder somewhere on the main page and call the jQuery load function when the button is clicked.
// in this case the button id is 'btnAddItemSelling' 
// and I use a div with id 'itemSellings' for the placeholder
$("#btnAddItemSelling").click(function () {
 $("#itemSellings").load("@Url.Action("CreateEditPartial","ItemSellings")");
});
The url passed in the function parameter is the url used by GET controller action for displaying the partial view that we will create next.

Then we create the controller actions. We have two controller actions, one for displaying the partial view (GET) and the other for receiving the form data after submitted (POST). Note that the actions return partial view type when displaying and when the validation is failed. Also on line 9, we could pass a model as a parameter to the method from the Ajax form.
public ActionResult CreateEditPartial()
{
 // prepare and populate required data for the input fields
 // . . .

 return PartialView("CreateOrEdit");
}

[HttpPost]
public ActionResult CreateEditPartial(ItemSellingDto itemSellingDto)
{
 if (!ModelState.IsValid)
 {
  // prepare and populate required data for the input fields
  // . . .

  return PartialView("CreateOrEdit");
 }
 else
 {
  return Json(itemSellingDto);
 }
}
For the validation, in this case I just simply used Data Annotation but we could use other alternative such as a custom method, etc. I simply used   ModelState.IsValid   to check whether the inputs passed are fine. Then also put   @Html.ValidationMessageFor(...)   for each input field on the view. Note also that I enclosed the Ajax.BeginForm inside a div (please see line 3 and 34 on the partial view file above) and assign the Id to UpdateTargetId (one of the AjaxOptions properties) so that when input is not valid then the partial view content will be automatically put into the target element.

Next, include these two javascript references; jQuery and jQuery Unobtrusive Ajax on the main page. Do NOT put jQuery Unobtrusive Ajax on the partial page as this will cause the page to be submitted multiple times instead of once. Also write the javacript function that will be called when the validation from server side is successful. In this case, the code returns an object if validation is successful then the javascript function checks the data passed, if it is an object then add it to the listing table.



function addRow(response) {
 if(typeof response =='object')
 {
  // append object to table
  . . .
 }
}