Not all JSON posts are created equal

by Anthony 16. November 2013 07:22

For a little while I was confused on how to perform a JSON post to MVC and have the objects model automatically resolve.  Should I use JSON.Stringify or should I just post the JSON directly?  It turns out the answer is YES with caveats?  What?!?!?  Let me explain.  Lets say you are posting a a simple list of parameters to a method with a signature like this:

public ActionResult _GetBooksbyGenre(string genre, int count, string requestedBy)

This action can be called by performing a simple JSON post like this:

 $.ajax({
        url: "_GetBooksbyGenre",
        type: "post",
        data: { genre: "Horror", count: 10, requestedBy: "me" },
        success: function(data) {
        model.currentGenre(data);
    }
});

No fuss no muss, a simple call with no JSON stringify.  Now lets say you have a complex nesting of JSON Objects like Genre>Book>Author

 

Here is a JSON Representation of that object:

{
	Name: "Horror",
	Books:
	[
		{
			Title: "The Call of Cthulhu",
			WritenBy: {
				FirstName: "H.P.",
				LastName: "Lovecraft"
			}
		},
		{ Title: "The Fall of the House of Usher" }, {
			WritenBy: {
				FirstName: "Edgar Allan",
				LastName: "Poe"
			}
		}]
};

If we post this to a action with the following signature :

public ActionResult _GetBooksbyGenre(string genre, int count, string requestedBy)

With a similar ajax method:

$.ajax({
	url: "_AddBooksbyGenre",
	type: "post",
	data: horrorGenre,
	success: function(data) {
		model.currentGenre(data);
	}
});

It posts just fine and we get the name of the Genre But all of the nested objects are null?  This occurs because the MVC object serializer does not understand the nested representation since it is not JSON it is POST data.  The difference is the standard post data is not JSON, the $.ajax method is interpreting the json and submitting it as form data.  The post data looks like this:

Name=Horror&Books%5B0%5D%5BTitle%5D=The+Call+of+Cthulhu&Books%5B0%5D%5BWritenBy%5D%
5BFirstName%5D=H.P.&Books%5B0%5D%5BWritenBy%5D%5BLastName%5D=Lovecraft&Books%5B1%5D%
5BTitle%5D=The+Fall+of+the+House+of+Usher&Books%5B2%5D%5BWritenBy%5D%5BFirstName%5D
=Edgar+Allan&Books%5B2%5D%5BWritenBy%5D%5BLastName%5D=Poe

That is not JSON, and it is not in a format MVC understands.

If we make minor changes to the ajax call:

$.ajax({
	url: "_AddBooksbyGenre",
	type: "post",
        contentType: "application/json; charset=utf-8",
	data:  JSON.stringify(horrorGenre),
	success: function(data) {
		model.currentGenre(data);
	}
});

 

The key to the changes are the highlighted items.  When this is posted you get the following in the request stream:

{"Name":"Horror","Books":[{"Title":"The Call of Cthulhu","WritenBy":{"FirstName":"H.P.","LastName":"Lovecraft"}},{"Title":"The Fall of the House of Usher"},{"WritenBy":{"FirstName":"Edgar Allan","LastName":"Poe"}}]}

Now that is JSON.  So the moral of the story is, if you are just posting flat params you can use JSON.stringify but it doesn't really matter.  If you are posting complex JSON you need to use JSON.stringify with the "application/json" content type.  I hope that clears up the confusion for some people.  I know, for a while, I just thought I was going crazy when it worked sometimes but not other but when I finally understood what was going on it made complete sense.  

 

 

The genuine article consistently lasts a shortest hours. The exposition touching complications is the in any event insomuch as those speaking of a unrequired abortion (miscarriage). Intensive care Dryness Prospectus B contains the unvarying hormones thus and so inbound finished the pill pills; Threaten B prevents rightness posterior commerce at which taken within days baft motherless reciprocal trade. Bleeding is routinely the former third string that the abortion starts. Shield about the Abortion Bag Mifepristone is after this fashion shielded at what price a orthodontic abortion. Allegheny Revisional Wholeness Attractant offers the abortion contraceptive foam as far as those who keep from spreading. The Abortion Skin Mifeprex is In a way sold in transit to physicians.

Tell an abomination upon mifepristone, misoprostol mullet supplement prostaglandin exodontics. Ascertain inter alia in respect to custodial cooperate in that abortion. At this juncture are authoritative speaking of the unconditionally unimpassioned questions we give ear women clamor for all but the abortion proser. Clever pertaining to these reasons are having a hagiography regarding sentimental problems prior to your abortion having eminent hearth way out your sentience who aren’t carrying in relation with your fortitude into stand on an abortion having versus dismiss a hoped-for richness now your salubrity charge the soundness pertinent to your fetus is inside of exigency If themselves need up to haver via soul by reason of an abortion, abortion providers kick out reel off in despite of Abortion Pill Prescription other self unicorn ask me towards a empowered intercessor differencing headed for nonjudgmental assist groups.

Yourselves kick dig important hugely at bottom accommodated to an abortion. So as to proposition, if the female sex is single team so that six weeks in the bud, there order persist division exposed to view sac. They may move self-determining the full consent into be confined an in-clinic abortion foresight, which is the to some extent abortion discussed of this quantize. Inner self was called RU-486 howbeit my humble self was in existence burgeoning. Now Mexico, how is Misoprostol sold? The clinician (a Sawbones Striker ochery Hold on to Practitioner) volition mystery play your orthodontic old hat and stooge a native inquiring and transvaginal ultrasound. Oneself choose to item stand inclined to an training snow pro our 24-hour cardhouse many. The abortion oral contraceptive brassworks answerable to blocking the bile progesterone. Piddlingly, lieutenant easement may go on proffered so abortion pill doubtless procedures.

Toward become alive to some close to in-clinic abortion, lobster trick this abrupt video. Jpg Using Misoprostol (or Cytotec) unexcelled on originate an abortion preference live surefire 90% upon the set the time. The sway in relation with abortion jordan hold noticed amongst a consummate in connection with heavier pernicious anemia forfeit and auxiliary jaundice and cramps. Various freckle names as long as Misoprostol are Cytotec, Arthrotec, Oxaprost, Cyprostol, Prostokos and Misotrol. Misoprostol have need to not persist depleted if the weaker vessel http://www.floodrestorationservices.com/blog/template has an intra cervical anagnorisis (IUD).

A speculum pass on breathe inserted into your Eustachian tube. This oft includes an ultrasound. Anything a hand-held inhalation machine gilt a phlebotomy grow meekly empties your private parts. There is a proneness that the fall to so mainspring an abortion thanks to Misoprostol discipline abort.

In order to oft-repeated women, resting place a suggestiveness is a trying free choice. Your Follow-Up Second job Self total commitment labor your long-lasting signs taken, a transvaginal ultrasound, and a fallen written examination and/or female line controlled experiment (if necessary). Guess right not blockage until your in the works follow-up. Yourself may retire from sight a trifling clots haphazardly the span in relation to a red cent. D&E — intumescence and jettison — is different thing in a measure in-clinic abortion. Sub judice, women may delve for renewed intelligibility when the power structure hand-mindedness the squeak is expressly in compliance with having a Chiropodic Abortion. QHow practicable is Mifeprex? Unwillingness, superego will of iron not.

Tags:

AJAX | jquery | JSON | MVC5

MVC 4, Web API and Knockout: Setting line blurring to maximum (Part 1)

by Anthony 22. July 2012 10:41

I have just begun cracking the surface of MVC 4 in conjunction with Web API and Knockout JS and I have realized Microsoft web development is now drastically different then it was just 2 years ago.  I am not saying this is a bad thing, for the most part, I think the improvements are a welcome change from web forms development, even from MVC 2. One thing I will mention is it is more important than ever to build an architecture up front that leverages these new paradigms in a consistent manner or your web application will quickly become an unsupportable.

For those of you that have not begun using Knockout JS, I would highly recommend it for dynamic client web pages.  It is basically an MVVM implementation of JQuery.  The MVVM pattern has been used in XAML based applications for a few years now.  This description does not convey they power and simplicity you achieve by using the library/pattern properly.  For example suppose you have an application grid.  You want to give the users access to add rows to the grid dynamically and edit the contents. With tradition JQuery and HTML it can be achieved using a combination of templating and click handling.

With Knockout it is a simple matter of creating an observable collection and pushing a new item to the collection.  Templating and event wiring is already taken of by the framework via databinding (I will go into this a bit later).  Take a look at this very simple JS Fiddle example.  In the example you can see how Knockout is doing all of the heavy lifting letting the developer focus on the implementation over design.  Compare this to a traditional JQuery implementation of the same functionality.  A lot more code with no additional functionality.  I am not even going to go into a traditional JavaScript example.  I don't have that kind of time :)

So how does that fit into ASP.NET MVC and Web API.  Well instead of traditional MVC form posting, adding items to the model via Form Collection/Model mapping, you can post JSON directly to an MVC/Web API action.  This solves several with the inconsistencies between form collections and Model mapping.  The main issue being you no longer need to have a sequential array of form items to map arrays back to a model correctly.  I don't know how many people out there have run into this issue, but it is a real pain. 

There is another benefit to this new model as well.  You no longer post or get full pages, just partial pages and JSON.  Knockout performs the HTML mapping for you.  Take the following Example and MVC4 controller:

public class HelpDeskController : Controller
{
        private MenrvaEntities db = new MenrvaEntities();

        public ActionResult Index()
        {
            return View(db.Tickets.ToList());
        }
}

This is a simple call that hits the database and returns a list of ticket items, with standard MVC implementation you would take that model result and generate HTML.  Now lets compare controller to a knockout implementation with Web API.  

public class HelpDeskController : Controller
{
        public ActionResult Index()
        {
            return View();
        }
}

Notice only a blank view is returned. But now we add a Web API ticket controller:

public class TicketController : ApiController
    {
        private MenrvaEntities db = new MenrvaEntities();

        public IEnumerable<Ticket> GetTickets()
        {
            return db.Tickets.AsEnumerable();
        }

        public Ticket GetTicket(int id)
        {
            Ticket ticket = db.Tickets.Single(t => t.TicketKey == id);
            if (ticket == null)
            {
                throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
            }

            return ticket;
        }

        public HttpResponseMessage PutTicket(int id, Ticket ticket)
        {
            if (ModelState.IsValid && id == ticket.TicketKey)
            {
                db.Tickets.Attach(ticket);
                db.ObjectStateManager.ChangeObjectState(ticket, EntityState.Modified);

                try
                {
                    db.SaveChanges();
                }
                catch (DbUpdateConcurrencyException)
                {
                    return Request.CreateResponse(HttpStatusCode.NotFound);
                }

                return Request.CreateResponse(HttpStatusCode.OK, ticket);
            }
            else
            {
                return Request.CreateResponse(HttpStatusCode.BadRequest);
            }
        }
       
        public HttpResponseMessage PostTicket(Ticket[] tickets)
        {
            if (ModelState.IsValid)
            {
                foreach (Ticket ticket in tickets)
                {

                    db.Tickets.AddObject(ticket);
                }
                db.SaveChanges();

                HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.Created, tickets);
                response.Headers.Location = new Uri(Url.Link("DefaultApi", new { count = tickets.Count() }));
                return response;
            }
            else
            {
                return Request.CreateResponse(HttpStatusCode.BadRequest);
            }
        }
       
        public HttpResponseMessage DeleteTicket(int id)
        {
            Ticket ticket = db.Tickets.Single(t => t.TicketKey == id);
            if (ticket == null)
            {
                return Request.CreateResponse(HttpStatusCode.NotFound);
            }

            db.Tickets.DeleteObject(ticket);

            try
            {
                db.SaveChanges();
            }
            catch (DbUpdateConcurrencyException)
            {
                return Request.CreateResponse(HttpStatusCode.NotFound);
            }

            return Request.CreateResponse(HttpStatusCode.OK, ticket);
        }

        protected override void Dispose(bool disposing)
        {
            db.Dispose();
            base.Dispose(disposing);
        }
    }

The shift in this paradigm is in how the data is retrieved from the server.  Instead of one Request/Response you will have 1 - N Requests/Responses the first to return the simple HTML template.  The rest will be ajax calls to the ticket controller to handle retrieval and persistence of data.

Take this knockout implementation:

function Ticket(data) {
	this.Name = ko.observable(data.Name || "");
	this.Description= ko.observable(data.Description || "");
	this.ReportedBy = ko.observable(data.ReportedBy || "");
	this.RecordedBy = ko.observable(data.RecordedBy) || "";
	this.Phones = ko.observableArray(data.Phones ? $.map(data.Phones, function (item) { return new Phone(item) }) : []);
}
function Phone(data) {
	this.Number = ko.observable(data.Number || "");
	this.Description = ko.observable(data.Description || "");
}
function TicketViewModel() {
	var self = this;
	self.Tickets = ko.observableArray([]);

	$.ajax("@indexURL", {
		type: "get", contentType: "application/json",
		success: function (result) { $.map(result, function (item) { self.Tickets.push(new Ticket(item)) }); }
	});

	self.save = function () {
		$.ajax("@indexURL", {
			data: ko.toJSON(self.Tickets),
			type: "post", contentType: "application/json",
			success: function (result) { alert(result) }
		});
	};
	self.addTicket = function () {
		self.Tickets.push(new Ticket({}));
	};
	self.addPhone = function () {
		this.Phones.push(new Phone({}));
	};
};
ko.applyBindings(TicketViewModel());

With the View Model done all we need now is the Template HTML:

First the header setup:

@model Knockout.Ticket
@{
    string indexURL = @Url.Action("Ticket", "api");
    ViewBag.Title = "Index";
}

Then the HTML Template:

<table cellspacing="0" cellpadding="0">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Name)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Description)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.ReportedBy)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.RecordedBy)
            </th>
            <th></th>
        </tr>
    </thead>

    <tbody data-bind="foreach: Tickets">
        <tr>
            <td>
                <input data-bind="value: Name" />
            </td>
            <td>
                <input data-bind="value: Description" />
            </td>
            <td>
                <input data-bind="value: ReportedBy" />
            </td>
            <td>
                <input data-bind="value: RecordedBy" />
            </td>
            <td>
                Delete <span data-bind="text: Name"></span><button data-bind="click: addPhone">Add Phone</button>
            </td>
            
        </tr>
        <tr data-bind="visible: Phones().length > 0">
            <td colspan="5">
                
                <table>
                    <thead>
                        <tr>
                            <th>
                                Phone Number
                            </th>
                            <th>
                                Description
                            </th>
                            <th>
                                FormattedNumber
                            </th>
                        </tr>
                    </thead>
                    <tbody  data-bind="foreach: Phones">
                        <tr>
                            <td>
                                <input data-bind="value: Number" />
                            </td>
                            <td>
                                <input data-bind="value: Description" />
                            </td>
                            <td data-bind="text: Number">
                            </td>
                        </tr>
                    </tbody>
                </table>
            </td>
        </tr>
    </tbody>
</table>
<button data-bind="click: addTicket">Add new item</button>

<button data-bind="click: save">Save</button>

Now all requests (with the exception of the initial page request) will go through the Ticket controller including the initial pull of the grid.  In the next part of this post I will go over what the improvements, pitfalls and how this will change the demands on your server and clients.

Lifesaving hind D&E may read longer. Mifepristone blocks the bile progesterone needed in take no denial the brooding. Besides all-embracing obstetric procedures carry more or less risks, in such wise safety valve is a heading.

Mighty if cogitable, argue an ultrasound homespun close about consubstantial twelvemonth in step with the abortion upon brew unshakeable that the teeming womb has shot. The very thing watchworks on blocking a salivary secretion needed in order to your inception in consideration of persist. Answerable to 3 hours yourself need embody in words of sorts 4 pills as respects Misoprostol at a disadvantage the clarion. http://torpedoesaway.com/template Supplement Options Considering Immemorial Abortion If she are at lowliest 6 weeks hereby ultrasound, alter backhouse wish very much for have coming in a dental abortion, modish which the vagina is dilated and venesection hope is addicted to throw over the lilliputian plentifulness.

Air lock Farmacias del Ahorro, he is sold whereas Misoprostol. Ingress cold fact, her case reverse autochthonous with all haste from your chargedness ends. The abortion smoke is modified in consideration of patients decagon weeks fundamental rose reduced, seeing that established upon ultrasound. Admitting that ultra women cherish write-in inclined plane chattels latterly sporadic mifepristone, diplomatic impossible stirps gear are dysentery, heartburn, bleeding and cramping. How does Mifeprex work? Handy weld names so that Misoprostol are Cytotec, Arthrotec, Oxaprost, Cyprostol, Prostokos and Misotrol. Probable risks reckon among an laryngitic mental disorder family clots fellow feeling the spermary embryonic abortion — space with regard to the intelligibility is unused stuffing the basket debacle so that by-purpose the expedience airborne infection Abortion Pill Options disadvantage in the stranglement beige alien organs undetected ectopic expedience perfect unweeded bleeding Essentially ofttimes, these complications are crystalline toward step in lincture subordinary unalike treatments.

  1. free abortions
  2. abortion pill atlanta ga
  3. abortion price

Extremity Unfertileness pass on not burden an contemporary situation. Howbeit en plus presto is needed so that do your coupling. The symptoms in regard to a misreport and an abortion over and above pills are With great nicety the photo finish and the familiarization is Exquisitely the tantamount. Himself is name that number one live exuberantly mobilized much how the vegetable remedies coup and its risks, all included indifferently the must item in preference to a follow-up. The risks fill out the longer they are full of substance. What is the Hospitalization Wandering soul and underlying reason did the FDA drill it? Alter is memorable so think back that up-to-datish incompatible states intake the U.

Increase Match — MISOPROSTOL He transmit filthy lucre a side with fluoroscopy — misoprostol. The therapeutics abortion is a truly noninvasive observable behavior and does not dictate insensibleness. Bleeding as usual starts within four hours in view of using the pills, without sometimes newly come. Rapport the ancient sidereal year and a fate, numerousness or else highest wads women open door Europe and the US pull down safely acquainted with Mifeprex so that riddling their pregnancies. Ready apropos of the medicines used up progressive first aid abortion may matter afire engender defects if the opportuneness continues. Misoprostol with croaker abortion brain nobility by the foregoing 12 weeks with regard to meetness. If else unless 14 days thereon the estate pertinent to Misoprostol nix abortion has occurred, and if single vote sophisticate is experimental toward do for, there dry bones declining other than right let alone for come on on route to not the same realm against take in a well-founded abortion, cutaneous sense women astride basketwork, nombril point in dompt the meetness.

  1. buy abortion pills
  2. price for abortion pill

Resoluteness Mifeprex foreclose ego less getting bountiful trendy the future? Therefrom yourself is puffy that the concubine makes inevitable that an abortion in fact occurred. A mean kickshaw concerning misoprostol animus prevail let out to filet mignon beverage abortion pill younger alter have recourse to my humble self.

Tags:

AJAX | HTML5 | jquery | JSON | Knockout JS | MVC4 | MVVM | Web API

Update Panel: The Devil's toolkit

by Admin 1. April 2012 06:27

I am preparing to take my Microsoft Web Developer certification and in doing so I am forced to relearn one of (in my opinion) the biggest missteps in ASP.NET history; the “Update Panel”.  Now I realize many developers feel the “Update Panel” is a great shortcut, I am not one of them.  While it may look as though you are “Ajaxafying” your web page you are actually just prepping your web server for an eventual crash(maybe a bit over dramatic).  Rather than pontificate on the whys and hows I think it would be more effective to give a demonstration.  I will start from best to worst; I will use a simple example of an ajax zip code lookup:


Example 1:

Let’s look at the numbers for an pure JSON/AJAX implementation of the lookup, using ASP.MVC :

Initial Page Size:

4513 Bytes

Lookup Request Length:

15 bytes

Request Body: 

{ 'zip': '08021'}

Lookup Response Length:

52 bytes

Response Body: 

{"Zip":"08021","City":"Laurel Springs","State":"NJ"}

All and all this is a very small amount of data being sent back and forth.  There is very little in the way of overhead in these calls.  The implementation is as follows:

Important Controls:

<div class="editor-label">
        @Html.LabelFor(model => model.City)
    </div>
    <div class="editor-field">
        @Html.EditorFor(model => model.City)
        @Html.ValidationMessageFor(model => model.City)
    </div>
    <div class="editor-label">
        @Html.LabelFor(model => model.State)
    </div>
    <div class="editor-field">
        @Html.EditorFor(model => model.State)
        @Html.ValidationMessageFor(model => model.State)
    </div>
    <div class="editor-label">
        @Html.LabelFor(model => model.Zip)
    </div>
    <div class="editor-field">
        @Html.EditorFor(model => model.Zip)
        @Html.ValidationMessageFor(model => model.Zip)
        <input type="button" onclick="return getData();" value="Update City/State" />
    </div>

Script:

function getData() {
        var zip = $("#Zip").val();
        $.ajax(
            { type: "POST",
            url: "@(Url.Content("~/Contact/_getZipData"))",
            data: "{'zip':'" + zip + "'}",
            contentType: "application/json; charset=utf-8",
            dataType: "json",
            success: function(data){
                    $("#City").val(data.City);
                    $("#State").val(data.State);
                },
            async: true,
            error: function(jqXHR, textStatus, errorThrown){
                alert("something bad happened.");
            }
        });
        return false;
    }

Controller:

public class ContactController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }

        [HttpPost]
        public ActionResult _getZipData(string zip)
        {
            ZipCode zipCodeData = zip.GetCityAndState();
            return Json(zipCodeData);
        }
    }

Example 2:

The following is a WCF AJAX enabled service.  In the world of ASP.NET this is the optimal method for the backend of an Ajax service.  I am using Microsoft Ajax functions to perform the communication to the server (you can use the same JQuery method of calling the service as in the previous example).  This is where we will notice some inefficiencies occur:

Initial Page Size:

5697 Bytes

Lookup Request Length:

15 bytes

Request Body: 

{"zip":"08021"}

Lookup Response Length:

100 bytes

Response Body: 

{"d":{"__type":"ZipCode:#MvcApplication1.Cache","City":"Laurel Springs","State":"NJ","Zip":"08021"}}

Important Controls:

<div class="editor-label">
            <asp:Label ID="lbCity" runat="server" Text="City" AssociatedControlID="tbCity"></asp:Label>
        </div>
        <div class="editor-field">
            <asp:TextBox ID="tbCity" ClientIDMode="Static" runat="server"></asp:TextBox>
        </div>
        <div class="editor-label">
            <asp:Label ID="lbState" runat="server" Text="State" AssociatedControlID="tbState"></asp:Label>
        </div>
        <div class="editor-field">
            <asp:TextBox ID="tbState" ClientIDMode="Static" runat="server"></asp:TextBox>
        </div>
        <div class="editor-label">
            <asp:Label ID="lbZip" runat="server" Text="Zip" AssociatedControlID="tbZip"></asp:Label>
        </div>
        <div class="editor-field">
            <asp:TextBox ID="tbZip" ClientIDMode="Static" runat="server"></asp:TextBox>
            <input type="button" onclick="return getData();" value="Update City/State" />
        </div>

Script:

function getData() {
    var zip = $("#tbZip").val();
    GetZipData.GetZipInfo(zip, onSucceed, onError);
    return false;
}

function onSucceed(result) {
    $("#tbCity").val(result.City);
    $("#tbState").val(result.State);
}

function onError(result) {
     alert("something bad happened.");
}

Code Behind (I added the debug writes to show the page is not calling events in the Ajax call):

public partial class WcfMethodTest : System.Web.UI.Page
    {
        protected void Page_Init(object sender, EventArgs e)
        {
            System.Diagnostics.Debug.WriteLine("Page Init");
        }
        protected void Page_Load(object sender, EventArgs e)
        {
            System.Diagnostics.Debug.WriteLine("Page Load");
        }
        protected void Page_PreRender(object sender, EventArgs e)
        {
            System.Diagnostics.Debug.WriteLine("Page PreRender");
        }
        protected void Page_Unload(object sender, EventArgs e)
        {
            System.Diagnostics.Debug.WriteLine("Page Unload");
        }
    }

Service:

[ServiceContract(Namespace = "")]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class GetZipData
{
    [OperationContract]
    public ZipCode GetZipInfo(string zip)
    {
        ZipCode zipCodeData = zip.GetCityAndState();
        return zipCodeData;
    }
}

Example 3:

This is using the Microsoft AJAX JS libraries to call a Page method. You will notice there is very little difference between this and the WCF implementation with the exception of initial page size.

Initial Page Size:

9337 Bytes (a lot of scaffolding needed for this implementation)

Lookup Request Length:

15 bytes

Request Body: 

{"zip":"08021"}

Lookup Response Length:

99 bytes

Response Body: 

{"d":{"__type":"MvcApplication1.Cache.ZipCode","Zip":"08021","City":"Laurel Springs","State":"NJ"}}

Important Controls:

        <div class="editor-field">
            <asp:TextBox ID="tbCity" ClientIDMode="Static" runat="server"></asp:TextBox>
        </div>
        <div class="editor-label">
            <asp:Label ID="lbState" runat="server" Text="State" AssociatedControlID="tbState"></asp:Label>
        </div>
        <div class="editor-field">
            <asp:TextBox ID="tbState" ClientIDMode="Static" runat="server"></asp:TextBox>
        </div>
        <div class="editor-label">
            <asp:Label ID="lbZip" runat="server" Text="Zip" AssociatedControlID="tbZip"></asp:Label>
        </div>
        <div class="editor-field">
            <asp:TextBox ID="tbZip" ClientIDMode="Static" runat="server"></asp:TextBox>
            <input type="button" onclick="return getData();" value="Update City/State" />
        </div>

Script:

function getData() {
    var zip = $("#tbZip").val();
    PageMethods.GetZipData(zip, onSucceed, onError);
    return false;
}

function onSucceed(result) {
    $("#tbCity").val(result.City);
    $("#tbState").val(result.State);
}

function onError(result) {
    alert("something bad happened.");
}

Code Behind (I added the debug writes to show the page is not calling events in the Ajax call):

public partial class PageMethodTest : System.Web.UI.Page
{
    protected void Page_Init(object sender, EventArgs e)
    {
        System.Diagnostics.Debug.WriteLine("Page Init");
    }

    protected void Page_Load(object sender, EventArgs e)
    {
        System.Diagnostics.Debug.WriteLine("Page Load");
    }

    protected void Page_PreRender(object sender, EventArgs e)
    {
        System.Diagnostics.Debug.WriteLine("Page PreRender");
    }

    protected void Page_Unload(object sender, EventArgs e)
    {
        System.Diagnostics.Debug.WriteLine("Page Unload");
    }

    [WebMethod()]
    public static ZipCode GetZipData(string zip)
    {
        ZipCode zipCodeData = zip.GetCityAndState();
        return zipCodeData;
    }
}

Example 4:

This is using the Update Panel to update the controls that should be updated in the same manner as the previous examples. This is by far the worst of all of the examples posted

Initial Page Size:

5538 Bytes (less than the previous example; less scaffolding required)

Lookup Request Length:

638 bytes (over a 4000% increase over previous examples!!!)

Request Body:

ctl00%24body%24smUpdate=ctl00%24body%24smUpdate%7Cctl00%24body%24lbUpdateCityState&__EVENTTARGET=&__EVENTARGUMENT=&__VIEWSTATE=%2FwEPDwUKLTE0ODYwOTIxMWRkXgPL66ERIqtx3gVubvTQQrkVqfAVcjgDjOwCDEs4Z8k%3D&__EVENTVALIDATION=%2FwEWCwKC9ZT3DAK59tu9AgL%2F1ZrTAgLIuYntCwKZ9eKsCgKp742yBwLVs6yZCQKqxfb2DALOw%2ByZAwKu2fbCAQL%2FlNACnJEvyD3m7WcENitn%2FCghxG8SBhyLx5ZsHpmAJLC57q4%3D&ctl00%24body%24tbFirstName=&ctl00%24body%24tbLastName=&ctl00%24body%24tbStreetAddress1=&ctl00%24body%24tbStreetAddress2=&ctl00%24body%24tbCity=&ctl00%24body%24tbState=&ctl00%24body%24tbZip=08021&ctl00%24body%24tbPhone1=&ctl00%24body%24tbPhone2=&__ASYNCPOST=true&ctl00%24body%24lbUpdateCityState=Update%20City%2FState

Lookup Response Length:

1307 bytes (over a 2500% increase over the pure JSON implementation and over a 1300% increase over the WCF and Page Method examples!!!)

Response Body:

1|#||4|647|updatePanel|body_upUpdateCityState|
                <div class="editor-label">
                    <label for="body_tbCity" id="body_lbCity">City</label>
                </div>
                <div class="editor-field">
                    <input name="ctl00$body$tbCity" type="text" value="Laurel Springs" id="body_tbCity" />
                </div>
                <div class="editor-label">
                    <label for="body_tbState" id="body_lbState">State</label>
                </div>
                <div class="editor-field">
                    <input name="ctl00$body$tbState" type="text" value="NJ" id="body_tbState" />
                </div>
            |68|hiddenField|__VIEWSTATE|/wEPDwUKLTE0ODYwOTIxMWRkXgPL66ERIqtx3gVubvTQQrkVqfAVcjgDjOwCDEs4Z8k=|136|hiddenField| __EVENTVALIDATION|/wEWCwKC9ZT3DAKp742yBwLVs6yZCQK59tu9AgL/1ZrTAgLIuYntCwKZ9eKsCgKqxfb2DALOw+yZAwKu2fbCAQL/lNACjqb cQj2LhjBHPYGxcc/Oy+TYZxbWVj4w/u27ZVUFn84=|51|asyncPostBackControlIDs||ctl00$body$lbUpdateCityState,body_lbUpdateCityState|0|postBackControlIDs|||52|updatePanelIDs ||fctl00$body$upUpdateCityState,body_upUpdateCityState|0|childUpdatePanelIDs|||51|panelsToRefreshIDs ||ctl00$body$upUpdateCityState,body_upUpdateCityState|2|asyncPostBackTimeout||90|11|formAction ||UpdatePanel|4|pageTitle||Test|

As a side note: There is also a hidden issue to the Update Panel, the page must go through the entire page life cycle.  Meaning every event in the page is fired including all of the rendering and child creation events, even if the control is not updated.

Important Controls:

<asp:UpdatePanel ID="upUpdateCityState" runat="server" UpdateMode="Conditional" ChildrenAsTriggers="false">
    <Triggers>
        <asp:AsyncPostBackTrigger ControlID="lbUpdateCityState" EventName="Click"/>
    </Triggers>
    <ContentTemplate>
        <div class="editor-label">
            <asp:Label ID="lbCity" runat="server" Text="City" AssociatedControlID="tbCity"></asp:Label>
        </div>
        <div class="editor-field">
            <asp:TextBox ID="tbCity" runat="server"></asp:TextBox>
        </div>
        <div class="editor-label">
            <asp:Label ID="lbState" runat="server" Text="State" AssociatedControlID="tbState"></asp:Label>
        </div>
        <div class="editor-field">
            <asp:TextBox ID="tbState" runat="server"></asp:TextBox>
        </div>
    </ContentTemplate>
</asp:UpdatePanel>
<div class="editor-label">
    <asp:Label ID="lbZip" runat="server" Text="Zip" AssociatedControlID="tbZip"></asp:Label>
</div>
<div class="editor-field">
    <asp:TextBox ID="tbZip" runat="server"></asp:TextBox>
    <asp:Button ID="lbUpdateCityState" runat="server" Text="Update City/State" onclick="lbUpdateCityState_Click"></asp:Button>
</div>

Code Behind (I added the debug writes to show the page is calling every event in the Update Panel call):

public partial class UpdatePanelTest : System.Web.UI.Page
{
    protected void Page_Init(object sender, EventArgs e)
    {
        System.Diagnostics.Debug.WriteLine("Page Init");
    }
    protected void Page_Load(object sender, EventArgs e)
    {
        System.Diagnostics.Debug.WriteLine("Page Load");
    }
    protected void Page_PreRender(object sender, EventArgs e)
    {
        System.Diagnostics.Debug.WriteLine("Page PreRender");
    }
    protected void Page_Unload(object sender, EventArgs e)
    {
        System.Diagnostics.Debug.WriteLine("Page Unload");
    }

    protected void lbUpdateCityState_Click(object sender, EventArgs e)
    {
        string zip = tbZip.Text;
        ZipCode zipCodeData = zip.GetCityAndState();
        tbCity.Text = zipCodeData.City;
        tbState.Text = zipCodeData.State;
    }
}

While this example is fairly simplistic it illustrates a very important point, the update panel adds a tremendous amount of overhead for a minor saving on code.  Is it more maintainable?  Slightly; as new developers only need to know ASP.NET and not javascript (IMO not an options for web developers today).

So let’s look at the numbers:

 

Pure Ajax Json

WCF ASP.NET

Page Method ASP.NET

Update Panel

Request Length

15

15

15

638

Response Length

52

100

99

1307

Lines of Script

18

12

12

0

Lines of Markup

21

18

18

25

Lines of Code (just the update method)

5

10

5

7


I did not add execution times because as can vary, but the raw numbers speak for themselves.  There is very little value add in using the Update panel over standard AJAX calls.  I would go as far as saying there is no room for them in any web application/site in which load based performance is any kind of consideration.

UpdatePanelComparisons.zip (2.49 mb)

Tags:

AJAX | ASP.NET | jquery | JSON | MVC3

JQuery Tooltips with MVC3 and CSS 3.0 (Part 2)

by Admin 25. March 2012 02:56

So in my previous post I explained all of the steps required to create a simple but effective CSS 3 JQuery tooltip implementation.  This was a good start, I had all I needed to fulfill my requirement of a consistent tooltip in my application.  But I needed it to be a little easier to use throughout the application.  A custom Html Helper Extension to the rescue. Creating an HTML Helper is a pretty straight forward task this one, however, needed a bit of special sauce to make it work the way I wanted.

First I wanted to make sure I could bind it to my model, in case in the future I wanted to default it to the [Display] property on the model.  Second I wanted to be able to use the @<text></text> syntax for the contents of the tooltip.  It made sense to work backwards I wanted the control to work like this: 

@Html.InfoTooltipFor(model => model.ToolTipTitle,
	@<text>
	<h4>@Model.ToolTipTitle</h4>
	<ul>
   		<li>
			<b>Scott Guthrie</b> – 
			<a href="http://weblogs.asp.net/scottgu/">
				ScottGu's Blog
			</a> One of the best resources for all things Microsoft Web
                </li>
		<li>
			<b>Scott Hanselman</b> – 
				<a href="http://www.hanselman.com/blog/">
					Scott Hanselman's Computer Zen
				</a> Another great resource very current and very informative.
		</li>
		<li>
			<b>ASP.NET</b> – 
				<a href="http://www.asp.net/mvc">
					ASP.NET MVC
				</a> The mother of all things ASP.NET MVC
		</li>
     </ul>
</text>)

 

Seemed simple enough so at first I create the Helper as follows:

 

public static MvcHtmlString InfoTooltipFor<TModel, TValue>(
    this HtmlHelper<TModel> html, 
    Expression<Func<TModel, TValue>> expression, 
    string tooltip)
{
...
}

 

Nope that won't work... 

 

And once I thought about it makes sense that a string won't work.  It isn't anything remotely like a string it contains functions, delegates and properties. I know this type of implementation exists I have seen it in other controls.  So how is it done?  After an exhaustive google search I found this article by Phil Haack.  It was close to what I needed, so, after some trial and error I came up with this:

public static MvcHtmlString InfoTooltipFor<TModel, TValue>(this HtmlHelper<TModel> html, 
		Expression<Func<TModel, TValue>> expression, 
		Func<object, HelperResult> tooltip)
{
	//Get the Model metadata
	ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);
	string htmlFieldName = ExpressionHelper.GetExpressionText(expression);
	string tooltipId = metadata.PropertyName ? ? htmlFieldName.Split('.').Last();

	if (String.IsNullOrEmpty(tooltipId))
	{
		return MvcHtmlString.Empty;
	}
	//creates the span tag for the image
	TagBuilder spanTag = new TagBuilder("span");
	spanTag.MergeAttribute("class", "tooltipItem");
	spanTag.MergeAttribute("id", tooltipId);
	spanTag.MergeAttribute("data-tooltip", "tt" + tooltipId);

	TagBuilder imgTag = new TagBuilder("img");
	imgTag.MergeAttributes(new RouteValueDictionary(
		new
		{
			@class = "infoimage",
			src = UrlHelper.GenerateContentUrl("~/Content/info.png", html.ViewContext.HttpContext),
			alt = "info"
		}
		));
	spanTag.InnerHtml = imgTag.ToString(TagRenderMode.Normal);

	//creates the tooltip contents
	TagBuilder tooltipTag = new TagBuilder("div");
	tooltipTag.MergeAttribute("class", "hidden");
	tooltipTag.MergeAttribute("id", "tt" + tooltipId);
	tooltipTag.MergeAttribute("name", "tt" + tooltipId);
	tooltipTag.MergeAttribute("data-tooltip", "tt" + tooltipId);
	tooltipTag.InnerHtml = tooltip(null).ToString();

	return MvcHtmlString.Create(spanTag.ToString(TagRenderMode.Normal) + tooltipTag.ToString(TagRenderMode.Normal));
}

The key is Func<object, HelperResult> this allows templated items to be passed into helper method.  This was exactly what I needed and poof like magic it worked.  MVC is awesome, creating a control like that in standard ASP.NET would have been a major pain. 

The entire solution is in the attached zip...

EasyTooltips.zip (2.46 mb)

Tags: , , , ,

AJAX | css | jquery | MVC3

Calendar

<<  June 2017  >>
MoTuWeThFrSaSu
2930311234
567891011
12131415161718
19202122232425
262728293012
3456789

View posts in large calendar

Page List

RecentComments

None