Showing posts with label JQuery. Show all posts
Showing posts with label JQuery. Show all posts

Friday, 12 July 2013

WCF REST Based Service Call using JQuery.

Hello friends, I received lot of feedback for my earlier posts. This encourages me to keep writing. Thank you for your feedback.

Ok, let's come back. Today I am going to show you, How to consume WCF REST Service using jQuery.

Problem Scenario:  
Assume we have a page which has three dropdowns to select Country, State and than City. Initially only Country dropdown values will be populated with page load but others will not. On selection of Country, State dropdown values should be populated for the selected country and same for City dropdown. Though the code which I am going to show here is different from the scenario I discussed here but it will help you to solve this kind of problem.

Solution: 
Below sample code shows WCF REST service example which will be consumed through jQuery. Here in my service I have two methods, One returns list of cities as json serialized string object and the other method takes some input (json serialized string) and returns a json serialized string.
Code Sample for Service Definition.:
A
and here the service method implementation.

Note: new JavaScriptSerializer().Serialize(object) is used to convert a object to json serialized object.
Now let's see how we can call to these methods using jQuery.

GET Request
Here we will see, how to make a GET request to call WCF REST based service method GetCities which returns list of city in a json serialized string form. Below is the code sample to do this.

 $.ajax({
        type: "GET", //- Type of request - Optional
        url: 'http://localhost:84/RESTService.svc/cities', //A string containing the URL to which the request is sent.
        dataType: "json", //data type returns from the server
        success: function (response) { //A callback function that is executed if the request succeeds.
            var result = jQuery.parseJSON(response);
            var html = "List of Cities are: <br\>";
            for (var i = 0; i < result.length; i++) {
                if(result[i].Value!="-1")
                    html += result[i].Value + "<br\>";
            }
            $("#div2").html(html);
        },
        error: function (xhr) {
            $("#div2").html("An error occured: " + xhr.status + ':' + xhr.statusText);
        }
    });


Here in above example I specified request type, request url and datatype to the method $.ajax(); Most important thing here is the request url. If url is not found or wcf service is not available in that case it will throw error. So here I also handled error as well. Next if service call is successful then control will fall under success callback function. As we are getting a serialized json object in string form from the server so we need to parse it to json first (because it's a object collection.) and that I am doing here using jQuery.parseJSON() method.
Now we have a json object as result value, shown below:
>> result
[[object Object],[object Object],[object Object],[object Object],[object Object]] {
    [0] : {...},
    [1] : {...},
    [2] : {...},
    [3] : {...},
    [4] : {...},
    [prototype] : []
}

It says we have five Name and Value pair object as per WCF REST Service method. We can access these value by writing code like result[0].Value and result[0].Name.
Important points to take care:
1. Here the Name and Value properties are case sensitive as per the properties definition in WCF REST Service method. So if we write result[0].value to access value then it will not return any value.
2. Another very important point here is, we are parsing the json string directly using response object.
 var result = jQuery.parseJSON(response);
and we have the value of response coming back is:
>> response
"[{"Name":"Select City","Value":"-1"},{"Name":"Bangalore","Value":"Bangalore"},{"Name":"Chennai","Value":"Chennai"},{"Name":"Goa","Value":"Goa"},{"Name":"Delhi","Value":"Delhi"}]"

The point which I want highlight here is, the response json string is not wrapped. Because in the WCF REST Service it is mentioned, not to wrap the response by using below attributes value:
BodyStyle=WebMessageBodyStyle.Bare,. If it would be wrapped then the output string would be bit different as shown below:
>> response
{
    [prototype] : {...},
    GetCitiesResult : "[{"Name":"Select City","Value":"-1"},{"Name":"Bangalore","Value":"Bangalore"},{"Name":"Chennai","Value":"Chennai"},{"Name":"Goa","Value":"Goa"},{"Name":"Delhi","Value":"Delhi"}]"
}

In this case we would have modified few line of code like this:
var result = jQuery.parseJSON(response.GetCitiesResult )
result[0].Name


POST Request
Now we will make a POST request and we will send a json serialized string to WCF REST service method and based on that response will be returned back again as a json serialized string form.
 Let's see the below sample code for POST request:
  var JSONObject = { "userData": { "FName": "Binod", "LName": "Mahto"} };
    $.ajax({
        type: 'POST',
        url: "http://localhost:84/RESTService.svc/Message",
        data: JSON.stringify(JSONObject),
        contentType: "application/json",
        dataType: "json", //Expected data format from server
        processdata: true, //True or False
        success: function (response) {
            $("#div1").html(response.GetWelcomeMessageResult);
        },

        error: function (xhr, ajaxOptions, thrownError) {
            $("#div1").html("An error occured: " + xhr.status + ':' + xhr.statusText);
        }
    });

In above example I mentioned request type as POST. There are few points which we have to take care is:
1. data: which we need to send as POST request should be json serialized string form. Which we are doing here using JSON.stringify() method.
2. content type: It must be "application/json". Instead of this if we write only "json" it will throw error as "400:Bad Request".
3.  datatype: It must be "json" only. Instead of this if we write "application/json" then it will throw error by saying "200:OK" but surprisingly error object 'xhr' will have the response output as well.
>> xhr
{
    [Methods] : {...},
    [prototype] : {...},
    readyState : 4,
    responseText : "{"GetWelcomeMessageResult":"\"Hi Binod Mahto Current time is:10:12 AM\""}",
    status : 200,
    statusText : "OK"
}

So to avoid this kind of head banging issue datatype's value must be written as "json" only.
4. Response output is wrapped here. If you see the response output here, you will find there is name attached with response string as "GetWelcomeMessageResult". And this happened because in the WCF REST method specifically it is mentioned to wrap the response object (by using attribute value BodyStyle = WebMessageBodyStyle.Wrapped). And the catch here is, the extra string which will be added to json string will have concatenation string of method name and 'Result' string. for Ex: GetWelcomeMessageResult. 
5. Another important thing here is that when you send the json serialized object (as I am sending userData here) to the server then it must be wrapped with the exact parameter name mentioned in the WCF REST service method.
 var JSONObject = { "userData": { "FName": "Binod", "LName": "Mahto"} };
In my case here my WCF REST Service method expects UserData object as per data contract.
    [DataContract]
    public class UserData
    {
        [DataMember]
        public string FName { get; set; }

        [DataMember]
        public string LName { get; set; }
    }
  

and the parameter name in service method is mentioned as "UserData userData". So suppose if I change my JSONObject value as 
var JSONObject = { "UserData": { "FName": "Binod", "LName": "Mahto"} };  then WCF Service method will be called but userData parameter value will be null. So we should keep this in mind that parameter name is tightly coupled with the json request format.

I guess I mentioned all the important points here for which generally people has to struggle a lot. But still if I missed something please feel free to mention those as your comment/feedback.

Thank You 

Wednesday, 22 August 2012

CRUD operation using MVC3 and Jquery


Frnds,
In this post I am gonna write code which is explaining CRUD operation using MVC WebGrid and Jquery.
Below Image Gallary shows Grid Data with Paging and Sorting functionality.
   
In this post I am gonna write code which is explaining CRUD operation using MVC WebGrid and Jquery.
 Before proceeding further we need to have our model class. Here is the our model class.
StateModel.cs
namespace MyMVC3WebApp.Models
{
    public class StateModel
    {
        [ScaffoldColumn(false)]
        public int StateID { get; set; }

        [Required]
        [StringLength(10)]
        [Remote("StateNameExist","State")]
        public string StateName { get; set; }

        [Required]
        [StringLength(10)]
        public string CountryName { get; set; }
    }
}
In our above model class I have implemented basic validation including Custom Remote validation which actually checks
State Name existence into the DB before submitting form. Here Remote attribute has two parameters, first one is action method name and the second one is the Controller name.
Above code will help you to understand the basic CRUD operation for data. Please give your feedback if you really like it or if you have any suggestion here.
Now let’s create our controller class which will have the code for the database operation.
StateController.cs
namespace MyMVC3WebApp.Controllers
{
    public class Country
    {
        public string Name { get; set; }
    }

    public class StateController : Controller
    {

        private TestDataEntities db = new TestDataEntities();
      //Method which returns list of country for dropdown country selection.
        public JsonResult GetCountries()
        {
            List<Country> resultset = new List<Country>() {
                new Country(){Name="India"},
                new Country(){Name="US"},
                new Country(){Name="Australia"},
                new Country(){Name="Rusia"},
            };
            return Json(resultset, JsonRequestBehavior.AllowGet);
        }
//Method to check state name existence being called by Remote attribute implemented in Model class for validation.
        public JsonResult StateNameExist(string stateName)
        {
            int count = (from states in db.StateMasters.AsEnumerable()
                         where states.StateName.ToLower().Trim() == stateName.ToLower().Trim()
                         select states.StateName).Count();
            if (count > 0)
                return Json(string.Format("State name {0} already exists.", stateName), JsonRequestBehavior.AllowGet);
            else
                return Json(true, JsonRequestBehavior.AllowGet);

        }
//Method returns list of states.
        public ActionResult Index()
        {
            IEnumerable<StateModel> states = from st in db.GetAllStates()
                                             select new StateModel() { StateID = st.StateID, StateName = st.StateName, CountryName = st.CountryName };
            return View(states.ToList<StateModel>());
        }

        //
        // GET: /State/Details/5

        public ActionResult Details(int id)
        {
            return View();
        }

        //
        // GET: /State/Create

        public ActionResult Create()
        {
            return View(new StateModel());
        }

        //
        // POST: /State/Create

        [AcceptVerbs(HttpVerbs.Post)]
        public void AddNewState(string stateName, string countryName)
        {
            db.AddState(stateName, countryName);
        }

        [AcceptVerbs(HttpVerbs.Post)]
        public void UpdateState(int id, string stateName, string countryName)
        {
            StateMaster state = db.StateMasters.Single(s => s.StateID == id);
            state.StateName = stateName;
            state.CountryName = countryName;
            db.SaveChanges();
        }

        [HttpPost]
        public ActionResult Create(StateModel stateModel)
        {
            try
            {
                if (ModelState.IsValid)
                {
                    int result = Convert.ToInt32(db.AddState(stateModel.StateName, stateModel.CountryName));
                }

                return RedirectToAction("Index");
            }
            catch
            {
                return View();
            }
        }

        public ActionResult Delete(int id)
        {
            db.DeleteState(id);
            return RedirectToAction("Index");
        }
    }
}
Now this is the right time to create our view as we have ready with our model and controller class.
Index.cshtml

@model IEnumerable<MyMVC3WebApp.Models.StateModel>
@{
    ViewBag.Title = "Index";
}
<h2>
    Index</h2>
<link href="http://1.cdn.blog.com/wp-admin/../../Content/themes/base/jquery.ui.all.css" rel="stylesheet" type="text/css" />
<link href="http://1.cdn.blog.com/wp-admin/../../Content/themes/base/jquery.ui.dialog.css" rel="stylesheet" type="text/css" />
<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
<script src="http://1.cdn.blog.com/wp-admin/../../Scripts/jquery-ui-1.8.11.js" type="text/javascript"></script>
<style>
    body
    {
        font-size: 82.5%;
    }
    label, input
    {
        display: block;
    }
    input.text
    {
        margin-bottom: 12px;
        width: 95%;
        padding: .4em;
    }
    fieldset
    {
        padding: 0;
        border: 0;
        margin-top: 25px;
    }
    h1
    {
        font-size: 1.2em;
        margin: .6em 0;
    }
    div#users-contain
    {
        width: 350px;
        margin: 20px 0;
    }
    div#users-contain table
    {
        margin: 1em 0;
        border-collapse: collapse;
        width: 100%;
    }
    div#users-contain table td, div#users-contain table th
    {
        border: 1px solid #eee;
        padding: .6em 10px;
        text-align: left;
    }
    .ui-dialog .ui-state-error
    {
        padding: .3em;
    }
    .validateTips
    {
        border: 1px solid transparent;
        padding: 0.3em;
    }
    .odd
    {
        background-color: #FFF;
    }
    .even
    {
        background-color: #E8E8E8;
    }

    .grid
    {
        margin: 4px;
        border-collapse: collapse;
        width: 600px;
    }
    .head
    {
        background-color: #E8E8E8;
        font-weight: bold;
        color: #FFF;
    }
    .grid th, .grid td
    {
        border: 1px solid #C0C0C0;
        padding: 5px;
    }
    .alt
    {
        background-color: #E8E8E8;
        color: #000;
    }
    .gridItem
    {
        width: auto;
        font-weight: normal;
        font-style: normal;
    }

    .gridHiddenItem
    {
        visibility: hidden;
    }

    .img
    {
        width: 20px;
        height: 20px;
        text-align: center;
    }
</style>
<script type="text/javascript">
    $(function () {  //$(document).ready(function () {

        //Start-apply css on table data
        $('tr:even').addClass('even');
        $('tr:odd').addClass("odd");
        //End-apply css on table data

        //Start-a workaround for a flaw in the demo system (http://dev.jqueryui.com/ticket/4375), ignore!
        $("#dialog:ui-dialog").dialog("destroy");
        //End-a workaround for a flaw in the demo system (http://dev.jqueryui.com/ticket/4375), ignore!

        //Start-get controls
        var state = $("#stateName"),
   country = $("#countryName"),
            btnDel = $('#deleteRecord'),
   allFields = $([]).add(state).add(country),
   tips = $(".validateTips");
        //End-get controls

        //Start-Json Call to get all country to fill the country dropdown.
        $.getJSON('/State/GetCountries', '',
        function (obj) {
            var html = "";
            for (var i = 0; i < obj.length; i++) {
                html += "<option value='"
                    + obj[i].Name
                        + "'>" + obj[i].Name
                            + "</option>";
            }
            country.html(html);
        }
        );
        //End-Json Call to get all country to fill the country dropdown.

        //Start-apply css for validation error message
        function updateTips(t) {
            tips
    .text(t)
    .addClass("ui-state-highlight");
            setTimeout(function () {
                tips.removeClass("ui-state-highlight", 1500);
            }, 500);
        }
        //End-apply css for validation error message

        //Start-length check validation
        function checkLength(o, n, min, max) {
            if (o.val().length > max || o.val().length < min) {
                o.addClass("ui-state-error");
                updateTips("Length of " + n + " must be between " +
     min + " and " + max + ".");
                return false;
            } else {
                return true;
            }
        }
        //End-length check validation

        //Start-state name existence validation.
        function checkIfStateExist(o) {
            var val = o.val();
            var IsExist = false;
            //synchroneous ajax call
            $.ajax({
                type: 'GET',
                url: '/State/StateNameExist',
                dataType: 'json',
                success: function (obj) {
                    if (obj == true) {
                        IsExist = true;
                    }
                    else {
                        o.addClass("ui-state-error");
                        updateTips(obj);
                        IsExist = false;
                    }
                },
                data: { stateName: val },
                async: false
            });
            return IsExist;
        }
        //End-state name existence validation.

        //Start-Convert div form to model dialog
        $("#dialog-form").dialog({
            autoOpen: false,
            height: 350,
            width: 250,
            modal: true,
            buttons: {
                "Submit": function () {
                    var bValid = true;
                    allFields.removeClass("ui-state-error");
                    //Perform Validation here..
                    bValid = bValid && checkLength(state, "State Name", 3, 45);
                    bValid = bValid && checkIfStateExist(state);
                    if (bValid) {
                        var id = $("#stateID").val();
                        var st = state.val();
                        var ctry = country.val();
                        if (id != "") {
                            url = "/State/UpdateState";
                            $.post(url, { id: id, stateName: st, countryName: ctry }, function (data) {
                                //reload data
                                location.reload();
                            });
                        }
                        else {
                            var url = "/State/AddNewState";
                            $.post(url, { stateName: st, countryName: ctry }, function (data) {
                                //reload data
                                location.reload();
                            });
                        }

                        $(this).dialog("close");
                    }
                },
                Cancel: function () {
                    $(this).dialog("close");
                }
            },
            close: function () {
                allFields.val("").removeClass("ui-state-error");
            }
        });
        //End-Convert div form to model dialog

        //Start-show dialog on create new button click
        $("#create-state")
   .button()
   .click(function () {
       $('#dialog-form').attr('title', 'Create New State');
       $("#dialog-form").dialog("open");
   });
        //End-show dialog on create new button click

        //Start-show dialog on Edit button click
        $("a#editRecord")
   .click(function (obj) {
       $('#dialog-form').attr('title', 'Edit State');
       $("#dialog:ui-dialog").dialog("destroy");
       var trObj = $(this).parents("tr");
       var id = trObj[0].cells[0].lastChild.defaultValue;
       var state = trObj[0].cells[1].innerText.trim();
       var country = trObj[0].cells[2].innerText.trim();
       $("#stateID")[0].value = id;
       $("#stateName")[0].value = state;
       $("#countryName").val(country);
       $("#dialog-form").dialog("open");
   });
        //End-show dialog on Edit button click

    });

    function ConfirmDelete() {
        return confirm('Do you want to delete this record');
    }

</script>

<p>
    <a id="create-state" href="#">Create New</a>
</p>
<div>
    <table>
        <tr>
            <td>
                @{
                    var grid = new WebGrid(Model, canPage: true, rowsPerPage: 5);
                }
                <div id="grid">
                    @grid.GetHtml(
                 tableStyle: "grid",
                headerStyle: "head",
                alternatingRowStyle: "alt",
                columns: grid.Columns(
                    grid.Column(null, null, format: @<input type="hidden" name="ID" value="@item.StateID"/>),
                    grid.Column("StateName", "State", style: "gridItem", canSort: true),
                    grid.Column("CountryName", "Country", style: "gridItem", canSort: true),
                    grid.Column("Edit", format: @<text><a id="editRecord" href="#"><img alt="" src="../../Content/images/Edit.jpg"
                        style="height: 20px; width: 20px" /></a></text>, style: "imgstyle"),
                    grid.Column("Delete", format: @<text><a href="@Url.Action("Delete", "State", new { id = item.StateID })" onclick="javascript:return ConfirmDelete();"><img
                        alt="" src="../../Content/images/delete.png" style="height: 20px; width: 20px" /></a></text>, style: "imgstyle")
)
                )
                </div>
            </td>
        </tr>
    </table>
</div>
<div>
    <div id="dialog-form" title="State">
        <p>
            All form fields are required.</p>
        <form>
        <fieldset>
            <input id="stateID" type="text" style="visibility: hidden" />
            <label for="stateName">
                State</label>
            <input type="text" name="stateName" id="stateName" />
            <label for="country">
                Country</label>
            <select id="countryName" style="width: 95%">
            </select>
        </fieldset>
        </form>
    </div>
</div>
Above view has style, script and html code for the view. starting of this post, post has the output images for the above code. Enjoy!