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

MVC Not the Magic Bullet

by Admin 1. July 2012 08:31

I have been working with ASP.MVC 3 fairly heavily for the past year and I am anxiously awaiting the production release of VS 11 and MVC 4.0.  I think ASP.MVC  is a fantastic platform and solves many of the issues that exist with ASP.Net webforms.  The issue that I have notice though is many people in the development community are touting it as a “Magic Bullet” of sorts.  I have heard “Development times will shrink when we move to MVC” or “This would be so much easier in MVC” or “You won’t have to worry about page bloat in MVC.”  These comments are really only half truths.  

Will development time speed up under MVC?  In the long run with competent architects, developers and designers timelines should shrink a small amount (I need more data to give exact numbers) in MVC. Developers can spend their time writing business logic without having worry about the impact to the UI (with some considerations).  Designers can focus on making the UI look good without worrying about to much ASP gobbledygook (with some considerations). In addition SEO competent designers will have a much easier time adding SEO compliance to the site (if that is your goal).

Will development get easier in MVC? Yes and no.  I would like to discuss this particular topic in three parts. First, there are many seasoned ASP.NET Web Form developers out there that are well versed in the page life cycle, web controls and the ins and outs of ASP.NET in general, and can deliver excellent web sites working in that framework.  MVC (razor in particular) will be a complete paradigm shift for them.  I have heard several developers compare it to classic ASP and they are a bit annoyed they are being pushed back in that direction.  This could not be further from the truth, but a cursory glance at MVC (especially non-razor) it is easy to see how you could jump to that conclusion.  The removal of view-state and control-state presents a new set of challenges when working with pages.  The heavy dependency on JavaScript and JQuery on validations, AJAX and UI will be a bit of a learning curve for web developers that have only had to deal with that on a cursory level.  It is now, in my opinion, impossible to keep developers shielded from JavaScript and to a point even CSS.  Most developers are already comfortable in these areas but I know of several that only use the two when absolutely necessary.

Secondly, MVC (and HTML 5) blurs the lines between behavior, formatting and content which traditionally follows this model:

Web Block

The new model looks something like this:

Web Venn

Leading to questions of what should I use and when?  There really is no hard and fast answer to that question.  I usually use the following as a guide line in order of importance?  

Target Browser Compatible > Robustness of Functionality > Ease of Implementation > Maintainability

This wasn’t as much of a concern in ASP.NET Webforms as many of the web controls used handled all three aspects in their server side properties.  Which brings me to my third topic; the lack of standardized custom controls with ASP.NET MVC.  With ASP.NET MVC there is no such beast as a “Web Control”, there are only HTML helpers, JavaScript and JQuery libraries.  The big component players (Telerik, Infragistics and ComponentOne) are providing some JQuery libraries and MVC libraries but not much that competes with the free items already out there also JQuery changes so often that using version based component specific JavaScript and CSS really limits your ability to keep up to date.  I found out the hard way trying to go with the old model of “buy rather than build” can severely limit your options.  Recently I have been going the way of “find and tweak rather than build from scratch.”  GitHub and CodePlex are becoming the new source of third party components.  These controls however are purely an implementation of HTML, JavaScript/JQuery and CSS.  They are not server code specific.  So throw out the idea of setting some properties in the designer and it just working.

Lastly, can developers be completely oblivious to page bloat?  Absolutely not, page bloat has just shifted from viewstate to JavaScript includes and CSS.  Finding all of the controls to replace existing libraries comes with the price of including more and more .JS files.  So if you think you are trading you 500k viewstate for 0k MVC, guess again.  If you are not careful it could be 500k viewstate for 1 meg of added script.  Luckily MVC 4 is working on combatting that issue with bundling, but that isn’t a magic bullet either.

The long and short; if you were slashing timelines and removing servers from you web farms because you thought all of your problems were solved.  You are gonna find yourself up “bit” creek without a paddle.

 

Tags: , , ,

ASP.NET | CodePlex | GitHub | HTML5 | MVC3

Calendar

<<  October 2017  >>
MoTuWeThFrSaSu
2526272829301
2345678
9101112131415
16171819202122
23242526272829
303112345

View posts in large calendar

Page List

RecentComments

None