Tales from the Evil Empire

Bertrand Le Roy's blog

News


Bertrand Le Roy

BoudinFatal's Gamercard

Tales from the Evil Empire - Blogged

Blogs I read

My other stuff

Archives

Using client templates, part 2: Live Bindings

In part 1, we saw how to use DataView to render JavaScript data using a simple template. In this post, we'll see how rich bindings unlock richer scenarios where user changes automatically propagate back to the data and to all UI that is bound to it.

In part 1, we used the simplest type of binding, one-way, one-time bindings. These bindings are created using the {{ expression }} syntax, which is both simple and rich in that it enables arbitrary JavaScript expressions. This is made possible by the fact that templates are compiled by the Ajax framework into a JavaScript function and these bindings get embedded right into that generated function. Because of that, the binding gets evaluated only once, when the template is instantiated.

For more complex scenarios where you need changes to the data to be automatically reflected by the UI or where you need bidirectional bindings, we are also supporting WPF's binding syntax. Let's use that syntax to modify the way the template in part 1 displays a person's name:

<legend>
<
span>{binding FirstName }</span>
<
span>{binding LastName }</span>
</
legend>

In order to be able to make changes to the data, I'll add a second DataView to the page, this one with input fields for the first and last names of each person in the data set:

<table>
  <thead><tr><td>First name</td><td>Last name</td></tr></thead>
  <tbody id="peopleTable" class="sys-template">
    <tr>
      <td><input type="text" sys:value="{binding FirstName}" /></td>
      <td><input type="text" sys:value="{binding LastName}" /></td>
    </tr>
  </tbody>
</table>

And now, without having written a single line of JavaScript code other than the DataView controls instantiation and data property setting, our boring read-only template became read-write. Any time the data in one of the input boxes changes, the binding will pick up the change and propagate it to the data. Our first template, because it was bound to the same data, will also get the changes automatically so that any change here:

ClientTemplateInput

will get reflected here:

TemplatePhotoLegendChanged

as soon as you focus out of the input field. One thing to notice is that even though you can explicitly set the binding direction on a binding, the framework does the right thing here by default by making bindings on input fields bidirectional and bindings on text node values unidirectional.

Oh, one last thing: just for fun, I took the screen shots in Google Chrome, where the new template stuff already works great.

The code in this post uses ASP.NET Ajax Codeplex Preview 2 the Microsoft Ajax Library Preview 6, which you can download here:
http://www.codeplex.com/aspnet/Release/ProjectReleases.aspx?ReleaseId=16766
http://aspnet.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=34488

The sample code for this post can be downloaded from here:
http://weblogs.asp.net/blogs/bleroy/Samples/TemplateLiveBindings.zip
http://weblogs.asp.net/blogs/bleroy/Samples/TemplateLiveBindingsPreview6.zip

UPDATE: fixed the code to use Preview 6 instead of preview 2.

Comments

Khurram Shahzad said:

Hey,

Many thanks very interesting article you posted.

But I was few questions regarding the web service.

Is it the normal Web Service (.asmx) or a JSon based web service.Another thing if the Web Service fails than How these kind of situations can be manipulated.

Overall, brilliant.

Regards,

Khurram.

# September 3, 2008 5:05 AM

Bertrand Le Roy said:

@Khurram: the service is a normal asmx service, *but* used in JSON mode. In ASP.NET, any web service can be very easily morphed into a JSON service by adding a [ScriptService] attribute. ASP.NET even generates an optional JavaScript proxy (that we're using here) that is accessed using the PeopleIKnow.asmx/js url (notice the /js at the end).

You can provide callbacks for failures or timeouts of the web service as additional parameters to the service call.

# September 3, 2008 12:16 PM

Odegaard said:

Just an idea... how about allowing for javascript expressions like this one:

<span>{Eval(Math.round({Binding Average})}</span>

It allows for some interesting binding scenarios and I've used it with great success in my own binding library.

# September 3, 2008 12:54 PM

Bertrand Le Roy said:

@Odegaard: in a one-way/one-time binding, you can do {{ Math.Round(Average) }}, but even with live bindings, you can provide a convert function (and also a convertBack function for two-way): {binding Average, convert=Math.round}.

# September 3, 2008 1:21 PM

HeartattacK said:

Changing the value in the textbox...it changes the javascript variables held in memory, right? Does the changes propagate to the server (or even the db)? I wouldn't think so. So, how are we to call our script service when a change is made? Are there any hookable event handlers that we can use that give us something like "newValues" and "oldValues" in javascript?

# September 3, 2008 9:32 PM

Bertrand Le Roy said:

Heartattack: that is of course an excellent question. There are mutation events that you can subscribe to on the JavaScript object and I'm going to cover that in the next post. To be clear, you are still using a plain JavaScript object, but we instrument it automatically when you do the binding so that it exposes change events.

In the next release, we're adding a DataSource control that handles those change notifications automatically and will be able to send them back to the server without requiring that you write any code.

# September 4, 2008 1:00 PM

John Lewicki said:

Looking good!  But!

I can't figure out the expression syntax to use of if my data item is an an array, as opposed to an object with named fields.  In other words, instead of an expression like {{Name}} I would like to use {{[0]}}.

It looks like the template compiler generates code that evaluates the expression inside of a 'with' block, which seems to break the array indexing inside the expression.  So I am currently resorting to a hack: {{ $dataItem[0] }}.

Is there/will there be an easier way to do this?

# September 8, 2008 4:26 PM

Bertrand Le Roy said:

@John: that is actually pretty uncommon, which is why we're not providing a specific syntax for that. {{ $dataItem[0] }} is actually not a hack at all but the supported way of doing that.

# September 8, 2008 4:40 PM

Matt Brooks said:

Hi Bertrand,

I only just got round to digesting these two posts on client templates. I think it is a necessary feature to aid client development, which when doing anything non-trivial can quickly become overly complex. I'm not sure I'm 100% happy with the template syntax but I can't think of any improvements and it sounds like you guys have debated it a lot! The actual binding syntax looks intuitive.

I've recently completed a project that required a certain degree of client side functionality that we don't get a great deal of help with when using ASP.NET. Among that functionality was a 'right-click-context-menu' for manipulating one-or-more rows of tabular data. I used a jQuery plugin to get the context menu up and running quickly. The menu options called back to an ASP.NET web service (script service?).

The approach worked really well for us. However, if data was updated on the server and the change needed to be reflected on the UI then we were forced to round-trip the current page, to keep view state updated.

I would be interested to know how the client templates play with view state? If I was to guess I would say that with the Web Forms model there will always be the problem of keeping view state in-sync.

Perhaps I am thinking about this all wrong though. I guess a more 'Web 2.0' approach would be to render a 'web form' (aka ASP.NET page) and leverage the power of client script to render out portions of the page and communicate changes back to the server. Therefore the page state is stored on the client and NOT on the server, hence removing the need for view state (?).

I look forward to your comments.

# September 10, 2008 5:34 PM

Oren Ben-Kiki said:

When pondering how extensively to use client templates, a big issue is search engine optimization (SEO). It seems to me that any data placed in the page using {{...}} will be invisible to search engines. This seems like a major restriction of the approach, limiting it to only minor parts of the page - basically anything that is very dynamic such as today's date, the current user name and so on.

I don't see any easy way around this problem... A pity. I was looking forward to having a lovely architecture where the DAL and business logic are all buried under a set of JSON data services and all the view layer is client-template HTML.

Or am I missing something here?

# October 4, 2008 1:51 PM

Bertrand Le Roy said:

@Oren: what you're saying applies not only to client templates but simply to all Ajax applications. For all resources that must be optimized for searching, all the info must be available from a simple GET request in the first place, which means that it has to be formatted on the server. Just don't render on the client what must be available on the GET request. That still leaves a lot of room for Ajax to improve the usability of applications.

# October 5, 2008 3:19 AM

Oren Ben-Kiki said:

I agree, the search engine problem holds for all Ajax code. It is just that client templates are very tempting for MVC architecture - move the V layer into the browser where it belongs. Alas, the SEO problem puts a strong limit on this.

A common workaround used by Ajax applications is to place the data in some invisible div element for the search engine. This means sending the same data twice into the page, or using DOM operations to access the data from the invisible div.

The latter is an interesting option, but it has two problems - first, creating data services that send the data as search-engine-friendly HTML is not automatic, while sending JSON/XML is, and second extracting the data from the DOM is awkward and inefficient compared to accessing JSON data.

Of course, one could envision some extension to ADO and client templates that would address both problems (except the efficiency issue perhaps, but browsers JS/DOM implementations are getting faster...). But I guess this is such a twisted solution that making it mainstream is not in the cards :-)

# October 5, 2008 4:28 AM

Derek said:

Hi Bertrand,

I've just read these posts and your MSDN magazine article on AJAX 4.0 and i'd like to congratulate you on your innovative approach to client side templating, looking forward to rolling it out.

Also i've noticed you weathering a fair share of negativity in various forums lately and i've got to say i'm pretty impressed with how you've kept it professional, kudos. Some of the SharePoint boys could take a few pointers from you - sorry, couldn't help myself :)

# October 5, 2008 5:46 PM

Bertrand Le Roy said:

@Oren: you make really good points here. We actually have a couple of prototypes along those lines. In particular, we have a server-side DataSource control that renders its data under the form of HTML tables, with a client-side parser that transforms that into JS data objects that can then be exploited client-side. You've got to be careful about how you hide that rendering of the data though as search engines tend to not index something hidden and can even consider that to be spam.

We actually alluded to that kind of approach in our roadmap document (publicly available from CodePlex). This kind of server-side integration is currently on the back burner though.

@Derek: thanks for the kind words.

# October 6, 2008 12:38 AM

gramic said:

I see that this usage is really nice. What about binding visible template without creating a new instance of it. For example 3 inputs for FirstName, LastName and Age with Save Button. Is it possible to use {binding } with the twoWay in this case and then eventually save the result?

# October 13, 2008 9:42 AM

Bertrand Le Roy said:

@gramic: You can create the Binding class from code and achieve that result. I'll also ask Dave whether that can be done declaratively by activating that part of the DOM. I'm not sure. Stay tuned.

# October 13, 2008 5:43 PM

Corey J gaudin said:

How do you 2-way bind a date? When I utilize this AJAX  Client Templating to bind to a textbox and add the jQuery Datepicker, it goes all nuts. Changing the Date on the textbox changes it to a string and doesnt really 2-way bind correctly. Is there some Datepicker associated with this library I can use to test so that it maps to and from the Javascript Object seamlessly?

# October 20, 2008 7:14 PM

Chris Bower said:

Hi Bertrand I have a couple of questions regarding what I should invest my time learning.

Should I go down the route of Ajax 4.0 or with Microsofts support for jQuery and from what I have seen from buying the book jQuery in Action, the ease at which you can use AJAX with jQuery. Also the use of jTemplates for dong the same as live bindings.

Which way should I go? Or am I being naive and really Ajax 4 has a lot more to offer.

# November 10, 2008 8:32 AM

Bertrand Le Roy said:

@Chris: it's actually not one or the other: jQuery has great selector support, which Microsoft Ajax does not, and Microsoft Ajax has a great component model, globalization and localization support, live bindings, ASP.NET integration and lots of other things that jQuery does not. I use both on many of my projects. A few things to consider: only the core jQuery file is supported by Mirosoft (hence jTemplate is not supported). Also, jTemplate does *not* do what live bindings are doing.

# November 10, 2008 12:38 PM

geek111222 said:

Hi,

My question is similar to that asked by 'John Lewicki'. I am trying to bind a dataitem which is basically a javascript object. I want to  refer its fields inside my templates. Like :

{{ dataitem.subitem }}

Is this possible ?

Can it be made possible in future by allowing child templates, that use 'dataitem' from the parent parent as source?

Also, is it possible to attach a client side callback function (just like the ItemDataBound) which can passed the 'dataitem' object as parameter.

# December 12, 2008 8:58 AM

Bertrand Le Roy said:

@geek111222: sure, you can do all that already.

The syntax in {{ }} is JavaScript so you can reference *any* object from there. For the current dataitem, you don't even need to dereference and you can do {{ subitem }} directly. But you could also have a global object and reference that.

Nested templates are also already supported. You can reference the parent template's data item using $parentContext.dataItem.

The DataView exposes an itemCreated event that gets a TemplateContext in the event arguments. You can get to the data item by doing args.get_templateContext().get_dataItem().

See weblogs.asp.net/.../instantiating-components-on-template-markup.aspx for an example of that.

# December 12, 2008 1:45 PM

Thanigainathan said:

Hi,

how this is different from JQuery based on the performance ?

Thanks ,

Thani

# March 24, 2009 5:07 AM

Bertrand Le Roy said:

@Thanigainathan: well, jQuery does none of that so it's a tough question to answer...

# March 24, 2009 4:25 PM

Monalisa1 said:

Hi Bertrand,

None of these work with preview 6 of same files. Please let me know if there any significant changes in syntac and what could be going wrong here.

# November 3, 2009 7:55 AM

Bertrand Le Roy said:

@Monalisa1: I updated the post to use Preview 6.

# November 3, 2009 5:01 PM

Peter Strong said:

Apologies if I'm missing something obvious but I can't figure out how to use a client template with an input such as a SELECT drop-down, ie where there's no "value=" attribute on the tag. I would like to bind data to a mix of inputs so the user can then edit the data, including SELECTs. I can't find any examples on the net of using templates with this sort of input field. Thanks in advance.

# March 10, 2010 1:51 PM

Bertrand Le Roy said:

@Peter: I wrote a post that hopefully answers your question... weblogs.asp.net/.../binding-a-select-in-a-client-template.aspx

# March 12, 2010 2:32 AM

Peter Strong said:

That's great. Many thanks for taking the time to write a post to explain it.

# March 15, 2010 11:13 AM