Archives

Archives / 2008 / November
  • Instantiating components on template markup

    All client-side template engines enable you to create HTML (feel free to go ‘duh’). What they don’t all allow is the creation of event handlers and components over the markup they generate. The general approach with those engines is to do a second pass of code over the markup to create handlers and components. This is quite unfortunate as this generally requires some knowledge of the markup (which plays against separation of concerns) or something like the introduction of marker CSS classes into the markup.

    For our own template engine, we wanted event handler creation and component instantiation to be first class scenarios, and we wanted both to be possible from imperative code as well as declarative code.

    Imperative code blocks

    Let’s start with the imperative approach. The template engine enables the introduction of code blocks right inside the template and also exposes a $element that is a reference to the last created element from the point in the template where it is referenced. So a first approach might be to just hook events and create components from there. Sure enough, that works:

    <img sys:id="{{ $id('photo') }}"
         sys:src="{{ 'Images/' + Photo }}"
         alt="{binding FirstName}" />
    <!--*
    $addHandler($element, "click", function() {
        alert("You clicked " + FirstName + "'s photo.");
    });
    *-->
    <br />
    <input type="text" id="{{ $id('firstName') }}"
           class="editInPlace name"
           value="{binding FirstName}" />
    <!--*
    $create(Bleroy.Sample.EditInPlace,
        { cssClass: "editing" }, {}, {}, $element);
    *-->
    

    Please note that the comment-based code block syntax is on its way out and will be replaced in the next preview with an attribute-based alternative. I will give more details when that is available. Suffice it to say for now that this mixing of component instantiation code into markup is not what we intended code blocks for. Code blocks are there to make simple scenarios like conditional rendering and looping over markup as easy as possible.

    Unobtrusive and imperative

    The better imperative approach looks very much like how you would do things outside of a template if you’re into unobtrusive JavaScript. The way you add event handlers and components over the page’s markup is by subscribing to some page ready event (pageLoad or Sys.Application.add_init in Microsoft Ajax, $(document).ready in jQuery). From that handler, you query the DOM for specific elements and add event handlers and components on those.

    To do the same over a template, you handle the itemCreated event of DataView, query the DOM that was just created for the data item and then add event handlers and components.

    One problem with repeating markup is to create unique IDs for the generated elements. This is relevant to the problem at hand because referencing elements by ID is by far the most common way. Wouldn’t it be nice to be able to just use getElementByID? Well, in client templates, we provide you with an easy way to both generate unique IDs and to reference elements by ID.

    Unique IDs can be generated by the $id function that is part of the execution environment of templates (along with $dataItem, $element, etc.). $id takes a string parameter, which is an ID that is unique within the template, and combines it with the current data item’s index to generate an ID that can be unique within the page:

    <img sys:id="{{ $id('photo') }}" sys:src="{{ 'Images/' + Photo }}"
         alt="{binding FirstName}" />

    To reference those elements -even if you don’t know the pattern $id uses to globalize the id-, you can use the getElementById method that is provided by the template context, which is conveniently available from the event arguments of itemCreated:

    args.get_templateContext().getElementById("photo")

    Here’s what the code to add an event handler and a behavior looks like:

    function onItemCreated(sender, args) {
        var context = args.get_templateContext(),
            dataItem = args.get_dataItem();
        $addHandler(context.getElementById("photo"), "click", function() {
            alert("You clicked " + dataItem.FirstName + "'s photo.");
        });
        $create(Bleroy.Sample.EditInPlace,
               { cssClass: "editing" }, {}, {},
               context.getElementById("firstName"));
    }

     

    Note: there is a known bug in Preview 3 that prevents getElementByID from working correctly outside of IE. We fixed that bug already for the next preview.

    Fully declarative

    Of course, if you prefer a fully declarative approach, we allow that too. The template engine understands DOM-0 event handlers in pretty much the same way that the browser does outside templates (we tried to apply the principle of least surprise here). This means that if you specify for example an onclick attribute on an element, it is understood as a string that is the source code for the body of a function that will act as a handler for the click event. The template engine also supports binding expressions on attributes and this is no exception. That means that you can actually build that string expression that will become the body of the handler dynamically using the current data item:

    <img sys:id="{{ $id('photo') }}" sys:src="{{ 'Images/' + Photo }}"
      alt="{binding FirstName}"
      onclick="{{'alert(\'You clicked '+FirstName+'\\\'s photo.\');'}}"/>

    Important note: you should be super-careful about building such handler strings on the fly with embedded data: there is potential for injection here, if the FirstName data came from the user or an uncontrolled source. In a real application, you'd want to encode FirstName to escape any quotes. You may use Sys.Serialization.JavaScriptSerializer.serialize(FirstName) for example. 

    Then, to instantiate the components, you can use our declarative syntax (which will also be the subject of a future post):

    <input type="text" id="{{ $id('firstName') }}" class="editInPlace name"
           value="{binding FirstName}"
           sys:attach="inplace" inplace:cssclass="editing"/>

    What it looks like

    Here’s what the page looks like (no matter which of the three versions of the page you choose):

    Conclusion

    There are plenty of options to add event handlers and components over template-generated markup in Microsoft Ajax, catering to different coding styles, but we hope we succeeded in keeping the system as simple as possible while keeping all relevant scenarios possible.

    Download the full code fro the demo here:
    http://weblogs.asp.net/blogs/bleroy/Samples/EventsAndBehaviorsInTemplates.zip

    Read more...

  • Should HTML be considered as a data format?

    As HTML is becoming more and more semantic, at least in intent, and all styling is moving into CSS, one has to wonder what it is now representing. It seems like it is now a format for unstructured data (a.k.a. rich text), in the same sense that XML and JSON are formats for semi-structured and structured data and CSV is a format for tabular data.

    Read more...

  • Simplifying the edit in place behavior

    Last week, I wrote about building a simple behavior to edit text in place. Almost at the same time, Nikhil was building a similar component for Silverlight, but it was considerably simpler because instead of substituting a label for the textbox on blur, he was just changing the border so that the textbox resembles a label. This is a lot simpler, cleaner and more stable. The textbox always behaves according to what one would expect from a textbox because it never ceases to be a textbox. No focus to manage, nothing to hide and show, just styles to change.

    Read more...

  • Getting a reference to a behavior

    In the last post, I showed how you can instantiate multiple behaviors on a single input element, through server extenders or directly through client behaviors (which themselves can be created imperatively or declaratively). In this post, I want to show how to get a reference to these behaviors.

    Of course, the simplest is to grab that reference when you create the behavior and hold on to it until the next time you need it:

    var tb1EditInPlace =
        $create(Bleroy.Sample.EditInPlace, { cssClass: "editInPlace" },
                {}, {}, $get('tb1'));

    Now of course, the code that needs to use a reference might not be from the same source or area of responsibility as the one that created it. If you created the behavior through declarative markup, for example (the framework creates the instance in this case, not your application directly).

    Fortunately, getting a reference to a behavior is very simple and can be done in a number of ways. First, you can give it an explicit id:

    $create(Bleroy.Sample.EditInPlace,
            { id: "tb1EditInPlace", cssClass: "editInPlace" },
            {}, {}, $get('tb1'));

    You can then retrieve it by just calling Sys.Application.findComponent, or $find for short:

    var ref = $find('tb1EditInPlace');

    Even if you don’t define an explicit id though, we’ll define one for you. The default id for a behavior is the id of the element concatenated with a ‘$’ character and the type name of the behavior. So if you defined the behavior this way:

    $create(Bleroy.Sample.EditInPlace,
            { cssClass: "editInPlace" }, {}, {}, $get('tb1'));

    You can then reference it like so:

    var ref = $find('tb1$EditInPlace');

    You can also define a name for the behavior instead of an id and this will pretty much replace the default (which we just saw is the type name):

    $create(Bleroy.Sample.EditInPlace,
        { name: "inplace", cssClass: "editInPlace" },
        {}, {}, $get('tb1'));

    And then reference the behavior this way:

    var ref = $find('tb1$inplace');

    The behavior is also added to the element as an expando attribute that has the same name as the behavior. This means that in the previous case, this will also work:

    var ref = $get('tb1').inplace;

    And in the ones before that:

    var ref = $get('tb1').EditInPlace;

    The key takeaway here is that it is always possible to get a reference to a behavior no matter how or where it was created.

    Read more...

  • Building a neat edit in place behavior

    For the purposes of my next post, I built a neat little edit in place behavior and I thought it deserved its own post. It does a pretty good job at showing how easy it is to build a clean behavior using ASP.NET Ajax. It’s always good to go back to the basics… In this post, I’ll show you how the behavior works, but more importantly how I built it.

    The behavior is pretty similar to the in-place editing behavior Nikhil built a while ago (but it works against the latest Ajax framework). It attaches to any text input or text area. In the absence of JavaScript, nothing happens and the input is directly usable, which makes for a graceful degradation story. When JavaScript is enabled, the behavior hides the input and replaces it with a span that has the same dimensions and the same contents. Clicking on the span hides it, brings back the input and gives it focus, enabling edition of the value. When focus moves away from the input, the behavior hides the input again, copies the new value into the span and brings it back. Please note that for accessibility, you can also tab to the span instead of clicking on it. Check out the video, it shows that much better than any text description ever could:

    The EditInPlace behavior does all that in 80 lines of readable code, doc-comments included. Before we look at some key parts of the code, let’s look at how you can use the control:

    <input type="text" id="email" value="bleroy@example.com"/>
    <script type="text/javascript" src="Script/MicrosoftAjax.js"></script>
    <script type="text/javascript" src="Script/EditInPlace.js"></script>
    <script type="text/javascript">
        Sys.Application.add_init(function() {
            $create(Bleroy.Sample.EditInPlace,
            { cssClass: "editInPlace" }, {}, {}, $get("email"));
        });
    </script>

    Include the library and behavior scripts, throw in some input or text area, $create the behavior over the input, setting properties such as cssClass if you have to. In the example, I’ve defined the style “editInPlace:hover” in the CSS so that the user gets this nice hint something might happen if you click when you hover over the control. This is the effect you see in the first few seconds of the video. Pretty simple.

    Of course, you can also use the new declarative syntax once you’ve included MicrosoftAjaxTemplates.js:

    <body xmlns:sys="javascript:Sys" sys:activate="*"
          xmlns:inplace="javascript:Bleroy.Sample.EditInPlace">
        <input type="text" id="email" value="bleroy@example.com"
               sys:attach="inplace" inplace:cssclass="editInPlace" />
    

    Here, the xmlns are the equivalent of server-side @Register directives in ASP.NET, sys:activate asks the framework to instantiate all declarative controls on the page and sys:attach does the actual instantiation. The properties of the behavior are set by using attributes with the prefix that we defined earlier for it. This declarative snippet is equivalent to the previous one that was using $create.

    Let’s now look at a few aspects of building this behavior. First, let’s look at the skeleton of the code:

    /// <reference name="MicrosoftAjax.js"/>
    Type.registerNamespace("Bleroy.Sample");
    Bleroy.Sample.EditInPlace = function(element) {
        Bleroy.Sample.EditInPlace.initializeBase(this, [element]);
    }
    Bleroy.Sample.EditInPlace.prototype = {
    }
    Bleroy.Sample.EditInPlace.registerClass(
    "Bleroy.Sample.EditInPlace", Sys.UI.Behavior);

    Here, we’re declaring the only dependency of this file using an XML doc-comment. This will point Visual Studio to the Ajax library so that we get IntelliSense when for example we type “Sys.” from within that file.

    Then, we’re declaring the Bleroy.Sample namespace, which is where we’ll build the behavior.

    The behavior itself is defined on the next line. A type in JavaScript and Microsoft Ajax really is the same objet as its constructor. Something to get used to… The only thing the constructor does for the moment is calling its base constructor using initializeBase. In its complete version, it also does some checking on the target element’s tag name to check it’s either a text area or an input, and it initializes private fields, which is good practice for a better and more predictable debugging experience.

    The next block is where the meat of the component is going to go: the prototype. This is where we’re going to define properties, events and methods.

    Last, we register the class with the framework using registerClass, pointing it to its base class, Sys.UI.Behavior. A behavior is a component that attaches to an HTML element, like a control, except that there can be only one control per element (DataView is a control for example) whereas there can be as many behaviors as you want per element (which enables for example a watermark, an auto-complete and an edit in place behavior to be combined on the same textbox).

    Let’s now look at the initialization phase of the behavior, where the events will get hooked from and the span will be created and added to the DOM:

    initialize: function() {
        Bleroy.Sample.EditInPlace.callBaseMethod(this, "initialize");
        var elt = this.get_element(),
            span = this._span = document.createElement("SPAN");
        var bounds = Sys.UI.DomElement.getBounds(elt);
        span.style.position = "absolute";
    Sys.UI.DomElement.setLocation(span, bounds.x, bounds.y); span.style.width = bounds.width + "px"; span.style.height = bounds.height + "px"; span.tabIndex = elt.tabIndex; span.className = this._class; Sys.UI.DomEvent.addHandlers(this._span, { click: this.beginEdit, focus: this.beginEdit }, this); elt.parentNode.insertBefore(this._span, elt); this._oldVisibility = elt.style.visibility; this._inputBlur = Function.createDelegate(this, this.endEdit); Sys.UI.DomEvent.addHandler(elt, "blur", this._inputBlur); this.endEdit(); }

    The method first calls base using callBaseMethod. Then it creates the span element and sets its position and size to be exactly equivalent to the input tag it will have to replace so that it doesn’t upset the layout of the page. It also sets the CSS class to the current value of the cssClass property (which is stored in the private field this._class). Two handlers are added to the new span: one for click and one for focus and both point to the same method, this.beginEdit. The way we’re creating the events is by using the very handy addHandlers method, which will attach several event handlers at once and will also take care of creating delegates with the specified context (this) so that the handler can have access to all the members of the behavior instance. Finally, the new span is added to the DOM right before the input element. Once that span has been created, we can store the old visibility mode of the input for later use, create a blur delegate for the input element that will call endEdit and then call endEdit to substitute the input for the span. Notice that we created the delegate manually rather than use addHandlers here. I’ll explain why in a moment.

    A good practice is to free all the resources and clean up after yourself from dispose. Let’s look how this is done:

    dispose: function() {
        if (this._span) {
            Sys.UI.DomEvent.clearHandlers(this._span);
            this._span.parentNode.removeChild(this._span);
            var elt = this.get_element();
            Sys.UI.DomEvent.removeHandler(elt, "blur", this._inputBlur);
            elt.style.visibility = this._oldVisibility;
            this._inputBlur = this._spanClick = this._span = null;
        }
        Bleroy.Sample.EditInPlace.callBaseMethod(this, "dispose");
    }

    Here we made sure that the method can be called multiple times by testing for the presence of what we’re going to clean. If the method is ever called again it will pretty much be a no-op.

    The dispose method starts by clearing all event handlers from the span element. It can do so by calling the handy clearHandlers method. We can afford to do that here because we completely own that element: we created it and managed it so we can safely use a very big hammer and crush all events without having to wonder if somebody else left their fingers on the table. For the input element that is quite different as there may be other behaviors attached to it that may have created their own events. That’s why here we’re targeting our cleaning efforts exclusively at what we created. This is why we created the delegate to endEdit ourselves so that we could keep a reference to it and use that later when removing the handler.

    Other tasks in dispose include removing the span from the DOM, resetting the input element’s visibility style and clearing private variables.

    And of course, the last thing we do is call base so that we can do our cleaning up without having to wonder if base already destroyed some of the objects we still need.

    Now that the initial setup and cleanup are in place, beginEdit and endEdit only have some trivial showing, hiding and focusing of the elements to do:

    beginEdit: function() {
        /// <summary>Puts the behavior in edit mode</summary>
        var elt = this.get_element();
        this._span.style.visibility = "hidden";
        elt.style.visibility = this._oldVisibility;
        elt.focus();
        this.raisePropertyChanged("isEditing");
    }
    endEdit: function() {
        /// <summary>Puts the behavior out of edit mode</summary>
        var elt = this.get_element();
        this._span.innerHTML = elt.value;
        this._span.style.visibility = this._oldDisplay;
        elt.style.visibility = "none";
        this.raisePropertyChanged("isEditing");
    }

    The only thing worth singling out here is that we’re taking care of triggering change notifications on isEditing every time the editing mode changes.

    The rest of the code is pretty trivial property accessors that are implemented using the get_ and set_ prefixes.

    I hope this helps.

    Get the full source code for the behavior and the sample page:
    http://weblogs.asp.net/blogs/bleroy/Samples/EditInPlace/EditInPlace.zip

    UPDATE: fixed a bug in the positioning of the span.

    UPDATE: Nikhil just blogged about building an edit in place behavior for Silverlight. I feel a little silly now because of the way he implemented that not by replacing the textbox with a label but by just making the border transparent on blur. This should work just as well in HTML and would make the code probably a lot simpler. I may try that when I have time. Anyway, check it out, great read as usual: http://www.nikhilk.net/Entry.aspx?id=214

    UPDATE: I rewrote the behavior to use Nikhil's trick of restyling the border: http://weblogs.asp.net/bleroy/archive/2008/11/24/simplifying-the-edit-in-place-behavior.aspx

    Read more...

  • Deep Zoom without Silverlight

    In a move that I wouldn’t have bet a dollar on, Live Labs released a purely JavaScript Deep Zoom client. You read that right, what was so far one of the nice features only found in Silverlight is now available in an open web, standards-based version.

    Of course, from a technical standpoint, Deep Zoom is just commoditizing what Google Maps made possible years ago in pure script so there wasn’t really a reason why this couldn’t be done, except smoother transitions and zooming but that’s pretty tenuous.

    The great thing about this new library is that the tools to create the Deep Zoom image are exactly the same as with Silverlight: the JavaScript client is pretty much a drop-in replacement for the Silverlight client.

    Embedding the viewer into a page is YouTube-easy: give the deep zoom url and it will build the code for you to embed.

    MSDN on Deep Zoom:
    http://msdn.microsoft.com/en-us/library/cc645050(VS.95).aspx

    Everything about the JavaScript client:
    http://livelabs.com/seadragon-ajax/

    Embedding the viewer:
    http://livelabs.com/seadragon-ajax/embed-viewer

    UPDATE: Kapil created a Python-based tile-cutting application that is compatible with both deep-zoom clients, to work around the Windows-only nature of the creation tools:
    http://blog.kapilt.com/2008/11/30/sharing-large-images-openlayers-gsiv-modestmaps-deepzoom-and-python/

    UPDATE: Also check out this TED talk to get a glimpse of the true potential of these technologies (all that you're seeing in this talk is publicly available by the way):
    http://www.ted.com/index.php/talks/blaise_aguera_y_arcas_demos_photosynth.html

    Read more...

  • One thing you didn’t know about ASP.NET unless you’re David Ebbo

    David has an excellent post about a pretty cool ASP.NET feature that you almost certainly don’t know about. I had no idea for sure. Check it out.

    http://blogs.msdn.com/davidebb/archive/2008/11/19/a-hidden-gem-for-control-builder-writers.aspx

    UPDATE: and there he goes again...
    http://blogs.msdn.com/davidebb/archive/2008/11/20/creating-a-controlbuilder-for-the-page-itself.aspx

    Read more...

  • PDC 2008 ASP.NET AJAX Futures talk available online

    The full 83 minutes of my PDC talk are available on the Channel 9 web site. You can watch the session online (using Silverlight) or download the video in a number of formats. Slides and source code for the demo are also available.

    PDC 2008 ASP.NET AJAX Futures

    Watch online:
    http://channel9.msdn.com/pdc2008/PC32/

    Download MP4 / iPod, Zune, WMV or WMV-HQ.

    Slides:
    http://mschnlnine.vo.llnwd.net/d1/pdc08/PPTX/PC32.pptx

    Demo code (contains MIT and MS-PL licensed JavaScript files):
    http://mschnlnine.vo.llnwd.net/d1/pdc08/ZIP/PC32.ZIP

    Another talk you may be interested in is Stephen Walther's. Stephen did an amazing job explaining how jQuery and ASP.NET Ajax work and fit together. The talk is very accessible even if you have no prior knowledge of jQuery: http://channel9.msdn.com/pdc2008/PC31/.

    Read more...