Sunday, February 24, 2008

UpdatePanel, Who?

I don’t know why people are so enamoured with ASP.NET AJAX.  Sure it works and is developed by Microsoft, but what’s wrong with the dozen other alternatives out there?  Many developers I talk to will say “yeah I used Ajax on this” (with a capital A) and what they are referring to is ASP.NET AJAX.  While I don’t think it is a horrible framework (I use it on occasion), I think it is too complicated and requires too much to get a simple example started.  When people say they have ASP.NET AJAX experience, it means to me that they have experience with declarative controls and <ScriptManager> tags.  It doesn’t necessarily mean they know how ajax works and it definitely doesn’t mean they know anything about javascript.  This worries me.

I often talk about how awesome prototype is (and lately I’ve been digging jquery) and inevitably someone tells me it’s just eaiser to wrap your sections in an UpdatePanel and be done with it.  It is sometimes hard to argue with that, even though it is the most inefficient way of doing ajax.  In addition, this can cause side-effects in WebForms because the entire lifecycle is being executed, even though only part of the page is rendered.  This method is actually called “partial html replacement” or “partial page rendering” and can be achieved easily without needing ASP.NET AJAX.

Now that ASP.NET MVC is out, which doesn’t work with ASP.NET AJAX (not the full stack) people are foaming at the mouth for their UpdatePanel equivalent.  (I think we’ll see an update for this in the coming weeks that will show ASP.NET AJAX working specifically in the MVC environment, but this point still stands.  You don’t have to use it.)

I’m going to use jquery in my sample, but the technique will apply to prototype, mochikit, and others.  In this sample we will take a functioning page and turn it into an ajax enabled page, while preserving the non-ajax scenario (if javascript is disabled).

For our example, I will borrow from an impressive rails tutorial [Quicktime] and implement the same thing in ASP.NET MVC.  The basic concept is this:  Search for flickr photos and display them on the page using ajax. 

We have our page:

Photo-search-form

if you search for “flowers”, you are taken to another action (physically) that shows you the results:

Photo-search-results

Now let’s assume that you want this to happen with ajax.  We’d like the results to be displayed directly on the same page as the query.

Here’s the form from that first action:

<h1>Photo Search</h1>
    <div class="box">
        <form id="search-form" method="get" action="/photos/find">   
            <label for="query">Query:</label>
            <input type="text" id="query" name="query" />
            <input type="submit" id="search" value="search" />
            <img id="indicator" style="display:none" src="../../content/load.gif" alt="loading...." />
        </form>
    </div>

And the action that the form submits to:

[ControllerAction]
public void Find(string query, string format)
{ 
     var flickr = new Flickr(ConfigurationManager.AppSettings["flickr.api.key"]);
     var photos = flickr.PhotosSearch(query, TagMode.AnyTag, query, 40, 1);

     ViewData["query"] = query;
     ViewData["photos"] = photos;

     RenderView("results");
}

(This uses the FlickrNet library available on CodePlex)

In order to translate this into an ajax-enabled page (while preserving no-javascript behavior) we have to do 2 things. 

  • Prevent the form from posting, so that the browser doesn’t take us off the page
  • Make the Find() action render only the partial html for the images (in the case of an ajax call

Let’s solve the first problem first.  Using jquery, we can capture the form submit by doing this:

function init()
{        
    $('#search-form').submit(        
        function() {
            $('#photos').hide();
            $('#indicator').show();  
            hijack(this, loadPhotos, "html"), 
            return false;
        });
}

$(document).ready(init);

When the document has been loaded, init() is called.  This finds our form and adds an onsubmit handler to it.  Next, we hide the photos div (in case there were pictures there already) and then we start the ajax indicator.  For the love of all that is good and holy, let the user know that you’re doing something!  Ok next up is this strange hijack() function.  This guy takes the form and submits it via ajax instead.  It accepts the form, the method to call when the result comes back (callback), and the data type of the return value.  Finally, to cancel the browser from actually submitting the form, we return false.

Here is that “photos” div I mentioned a second ago:  (I put this directly under our form)

<div id="photos" class="box" style="display: none">
</div>

It’s initially hidden because it will be empty when the page physically loads.

hijack() is a generic javascript function, and is defined as:

function hijack(form, callback, params, format)
{   
    try {                                            
        $.ajax({
            url:        form.action,
            type:       form.method,
            data:       $(form).serialize() + "&ajax=true",
            dataType:   format,
            success:    callback,
            failure:    function(e) { alert("error! " + e); }
        });
    } catch(e) { 
        alert(e);
    }
}

This is pretty basic ajax stuff in jquery.  The only thing different that I am doing here is notifying the controller that this is an ajax call, so that we may make it behave a little differently.  After all, we don’t want it to return a full html document… we only want the images!

That’s the essense of issue #2.  If we revisit our controller action, we can make the following change:

[ControllerAction]
public void Find(string query, bool? ajax)
{ 
    var flickr = new Flickr(ConfigurationManager.AppSettings["flickr.api.key"]);
    var photos = flickr.PhotosSearch(query, TagMode.AnyTag, query, 40, 1);

    ViewData["query"] = query;
    ViewData["photos"] = photos;
    
    
    if(ajax.HasValue && ajax.Value)
    {
        RenderView("_images", photos);
    }
    else
    {
        RenderView("results");    
    }       
}

We’ve added an optional parameter for the action to check for.  There are a number of other ways you might accomplish the same thing by embedding this into the controller itself.  You could make a convention that allows a base Controller class set an IsAjax boolean property to true if it detects this variable in form variables or querystring.  Whatever you find the cleanest.

And, Here is our new form!

Photo-search-ajax

You couldn’t see it in that still image, but there was an animated ajax indicator, and a nice slide down effect on the photo div.  If you turn off javascript, the whole thing still works.

This entire process is basically what you get with UpdatePanel.  The same techniques are used to accomplish partial page rendering.  Granted, UpdatePanel works slightly different (it’s parts of the same page that get updated) but you could easily return a full html document from the action and snip out the html you want to update.  Or you might have a specific ViewFactory that knows to only serialize HTML starting with an element root.

With some conventions and a tiny bit of javascript, you could easily achieve partial page rendering with out any fuss.  Plus you get to work with jquery, which is much mor enjoyable than ASP.NET AJAX.

UPDATE:  Download the code below.  You’ll need your own flickr api key (you can get one instantly from flickr.com).  Place it in the web.config.

PhotoSearch.zip (124 KB)

Saturday, January 12, 2008

Visual Studio 2008 CSS Support - Not Quite

One of the often-touted new features of VS 2008 is the improved CSS support.

I like having intellisense for CSS, sure, but I dont’ want to have a warning for classes that don’t exist…. like this:

Vs2008-css-warning

That green squiggly indicates a compiler warning.  It can’t find the CSS class, (which is referenced in the Master Page). 

But there are other reasons why you might want to define a class without defining an associated style.  You might be using it to distinctly select elements from the DOM using javascript. 

The ever-so-awesome prototype framework introduced this syntax:

$$(‘.info’)

which would select all of the elements with a class name of “info” for you to work with.

Just about every other javascript library out there now supports this syntax (or a similar one).

If this bugs you, you can turn it off by checking this box (in Tools -> Options) :

Vs2008-turn-off-html-errors

You may not want to do this, though…  it could potentially save you time if you’re looking for a rendering bug.

Another part of VS 2008 that I don’t really care about is this CSS designer.  I can’t imagine anyone who would actually use this.

I mean, come on… look at it!

Vs2008-css-builder

I guess if you don’t know CSS very well, you might want some options.  But that form is more confusing to me than border-top: solid 1px #999;.

(and one could argue, if you don’t know CSS, should you be defining styles at all?)

I really applaud Microsoft’s attempt to bring a better CSS development experience to the masses, but I think they missed the mark on this one.

Tuesday, November 06, 2007

Prototype & Scriptaculous - Protips #1

I often talk to people who haven't worked with one of the cool javascript frameworks out there.  I am partial to prototype and scripaculous, but I also really like MochiKit.

If you aren't taking advantage of one of these, then perhaps this post is for you... I give you the ultra quickstart to diving into prototype and scriptaculous!

Things you shouldn't ever program javascript without

The best thing ever invented since rubber tires: a replacement for document.getElementById('control_id');

You know you're not supposed to use document.all or document.ctrl_id, right?  Well at least now you do. document.getElementById() is the cross-browser safe way of getting an element off of the page. The prototype equivalent is $('control_id'). This will automatically extend the element returned with some helpful utility methods (like extension methods in C# 3.0). -- more on this later.

Along the same lines is a useful method for selecting elements using CSS selectors. If you're a CSS junkie (like me!) you'll appreciate this.    

$$("div span.info");
This will return all of the DOM elements that would be matched by that CSS Selector. (for those who don't know, this would get all span tags with a class of "info" that are directly nested underneath a div.

Want to show or hide an element? Use Element.hide(ctrl), Element.show(ctrl), or Element.toggle(ctrl). You can attach these methods onto the element itself by saying Element.extend(ctrl). If you retrieved it via the $() method, then your object already has these methods.

Let's say you want to grab all of the span tags from a parent div tag.  Typically you'd do this:

var spans = parentDiv.getElementsByTagName("span");

but what is spans?  If you thought it was an array, you'd be wrong.  It's a NodeList, which is not nearly as powerful as a javascript array.  (With javascript arrays you can push(), pop(), shift(), and other cool things that you can't do with a NodeList.  Anyway, back to the spans collection, we can elevate this to an array by using the $A( spans ) method.  Now we can use all of the nice array goodness for that list.

Effects for that extra polish

How about fading an element?

new Effect.fade(elem);
This also takes options so you can control how fast it fades, for example. Check the excellent docs for all the gory details.

Want to make a slide out box?

new Effect.toggle(elem, "slide");
This will toggle the element using slide up and slide down. ("fade" and "appear" also work here for toggling).

How about some ajax?

Forget UpdatePanels. Most of the time we just need to fire off a request and get a list of values back from the server. Prototype has you covered there:

new Ajax.Request( "/path_to_server/ajax/AjaxHandler.ashx?op=getUsername&value=bob",
{
method: "get",
onSuccesss: function(result) {
if(result.responseText) alert('available!); else alert('taken');
},
onFailure: function(e) { alert('something went wrong ' + e); }
}

I don't know about you, but that's pretty painless to me.

Want something a bit richer?  How about an autocomplete textbox? I wrote this one to auto-complete cities as you type.

var url = '~/AjaxHandler.ashx?op=get_cities';

ac = new Ajax.Autocompleter(city_textbox_id, results_id, url,
{
paramName: 'filter',
frequency: 0.2,
minChars: 1,
indicator: indicator_id,
callback : function(element, filter) {
var state = $(state_dropdown_id);
var state_code = state.options[state.selectedIndex].value;

return filter + '&state=' + state_code;
}
});

That's it. I'm not kidding. I handed it a textbox, a div to display the results, and a url to get the data. It takes care of all of the ajax requests, the frequency limiter to prevent us from firing a request with every single keystroke, and returning the results into a list. It even has full keyboard support. The url looks like ~/AjaxHandler.ashx?op=get_citiees&state=TX&filter=Hou and it will return the matches that it finds (based on an HttpHandler I wrote).

What are your favorite prototype / scriptaculous protiops?

Wednesday, October 17, 2007

Bandaids, Coke, QTips, Kleenex, and Ajax

 I’m sure you’ve noticed these…

  • Bandaid - Adhesive Bandage
  • Coke - Soda (at least here in the crazy south)
  • QTip - Cotton Swab
  • Kleenex - Tissue

The world doesn’t need another one:

  • Ajax - Microsoft ASP.NET AJAX

For the 100th time… AJAX does not mean Microsoft ASP.NET AJAX!

Need I say more?

Tags:
Sunday, August 26, 2007

HoustonTechFest 2007 was a Hit

Houston Tech Fest 2007 was yesterday and it was an amazing success.  Over 600 people were present and I was able to see a lot of old friends and meet some new & interesting people.

I got to chill out with Brad Abrams and a few other folks from Microsoft, and it was great to meet Scott Bateman (fellow Houston blogger and advocate of Continuous Integration).  His talk was most excellent, he was very informative and it showed that he not only understood the concepts, but he was able to explain it all in a way that (I thought) was easily grasped by the audience.

Some of the other speakers included Mike Azocar, John Cook, Tim Rayburn (sorry I missed your talk again Tim!), and David Walker.

Funny thing about Tim’s talk.  I was in the room next door listening to David Walker talk about SOA and WCF, and we could hear Tim through the wall partition.  At one point I heard Tim say, “If you can’t hear me in the back, please shout out and let me know.”  I felt like shouting from our room .

I had two sessions, Advanced CSS & Javascript and ORM with NHibernate.  In the first session, I had a full room (over 100 people!) and the audience was very engaging.  It’s great to have an audience that has so many questions and comments on what you are saying.  It becomes more of a dialog, which I think makes the content so much more valuable for everyone. 

Sometime in the midway point of the talk the projector just turned off.  It’s hard to give a talk on CSS without some visuals, so I continued typing and said “Imagine if you wil…” to a room full of laughter.  About that time the projector turned on again.  I didn’t find out until later, but my friend Wes had sat on the cord and unplugged it, then silently plugged it back in.  Funny how a room full of geeks and nobody thought to check the plug when it turned off.  Ah well.

At the end of the talk I had a couple people come up to shake my hand and a little 4–year old girl came up, touched my arm, then ran away giggling.  (Don’t ask me…).  I really enjoy giving this talk and I would like to branch outside of Houston and start giving it elsewhere around Texas (and the U.S.).

My second session was on NHibernate and it was more cozy, with only 15 people or so.  I think NHibernate is a lot harder to make interesting for an intro class because it is a radically different way of developing applications that most people are exposed to.  There is so much material to cover and only a short amount of time, so I tried to focus on the code and keep the examples short and simple.  I think it was effective, though I’d like to branch off this talk and start giving more advanced lectures on NHibernate.

Thanks to all who came to the event (especially my sessions!).  I hope you all had as much fun as I did.

The files from my presentations can be downloaded here:

File Attachment: AdvancedCSSAndJavascript.zip (1161 KB)

File Attachment: nhibernate.zip (4001 KB)

Also, a lot of people were interested in the tools I was using during my demos.  My favorite text editor is E, which you can get at http://www.e-texteditor.com.  My zooming tool that I use is called Zoomit.  My launching utility is called Launchy and it saves me so much time everyday.  I use ReSharper for Visual Studio nirvana (seriously, get this tool).

Some links I mentioned at the talks:

http://www.csszengarden.com

http://www.exploding-boy.com

http://www.mochikit.com

http://www.prototypejs.org

And finally, my favorite CSS book that I keep recommending to everyone:  CSS Mastery.

Friday, June 15, 2007

HDNUG Last Night

Last night I gave a talk to the largets crowd I have ever spoken in front of.  There were over 100 people in the room and they were a great audience.  I received lots of devoted attention and people were eager to participate.  I was quite nervous in the beginning, but once I got into the material I think it came across very well.

I spoke about why to use CSS and what we can do to overcome some of the pitfalls.  I also explained why we don’t want to be using tables for layout. 

I moved from there to some javascript techniques and a little bit of ajax.  From what I got from that portion, it could have probably warranted more time, but there was just so much to cover.

The files that I used during the demo are available for download here:

File Attachment: AdvancedCSSAndJavascript.zip (1140 KB)

 

I received a number of questions about the tools I was using during the demo.  The text editor I was using was e.  You can download it at www.e-texteditor.com.  The application launcher that I use is launchy.  It is available at www.launchy.net.  Finally the Keyboard shortcut window, called Key Jedi is available here.

If you attended the presentation, I would love to hear your feedback in the comments.  Thanks for coming!

Update:  The links that I wanted to show (but couldn't) during the presentation are: