LA.NET [EN]

MS AJAXArchive

Apr 14

It’s still not for sale, but you can be notified when it’s out. Btw, here’s its cover:

aspnet40book

Nov 30

I’d like to apologize for the lack of posts lately…no, I haven’t given up in my Silverlight series, but I’ve been busy updating my existing ASP.NET book to the 4.0 version. It seems like I will have a lot of work (much more than I had anticipated, but the truth is that I really enjoy writing about technical stuff) so the Silverlight series won’t really go as fast as the one on MS AJAX…sorry for that…now back to business :,,)

I really didn’t gave it any thoughts until I spoke with a friend who he asked me about the options we have for requiring a script (when using MS AJAX, of course!). So, lets see…we can specify it by:

  • using the scripts object : Sys.scripts.Core loads the basic MS AJAX JavaScript file
  • using the components object: Sys.components.dataView loads all the JavaScript files required for using the DataView component;
  • using the plugins object: Sys.plugins.bind will load all the JavaScript file required for using the bind method;
  • name: “History” will load all the scripts required for supporting the history feature.

All of them requires that you “define” your scripts in a JavaScript file which must be included in the page after the start.js file. And the best thing is that you can mix them all when using the Sys.required method. Fantastic, right?

Nov 24

Some observations on MS AJAX and JQuery integration

Posted in Javascript, MS AJAX       Comments Off on Some observations on MS AJAX and JQuery integration

In my last post, I’ve talked a little bit about the new plugins introduced by the latest release of MS AJAX (beta, at the time of writing). One of the things that didn’t enjoy much was that I had to call the JQuery’s get method in order to get a reference to the DOM element array. What I was forgetting was that MS AJAX has really good integration with JQuery (something that Dave Ward was kind enough to reminded me).

In practice, this means that I can simply pass a string with the selector I want and I’ll automatically get the correct DOM element array. here’s the updated code:

<script type="text/javascript">
  Sys.require(
        [Sys.scripts.jQuery, Sys.scripts.ComponentModel],
        function () {
          $.addHandler(
                "input",
                "click",
                function (evt) {
                  alert("JQuery: " + evt.target.id);
                });
        }
    );
  </script>

Notice that you can use more complex selectors too. All the selectors which can’t be interpreted by MS AJAX will be delegated to JQuery. As you can see, MS AJAX is becoming a really cool lib, don’t you think?

Nov 23

MS AJAX beta – new plugins

Posted in Javascript, MS AJAX       Comments Off on MS AJAX beta – new plugins

The MS AJAX beta release adds some new plugins to the previous ones. Besides the existing setCommand and bind, we now have access to the following plugins:

  • addHander: you can use this plugin to set up a handler for one event fired by one or several DOM elements;
  • removeHandler: as you can probably guess from its name, you can use this to cancel an existing DOM event subscription;
  • addHandlers: similar to the addHandler. However, in this case you can hook up several events (instead of passing the name of the event, you’re supposed to use a literal JS object where you specify the events and corresponding handlers);
  • clearHandlers: used for cancelling all event subscriptions that have been previously setup;
  • activateElements: used for activating the indicated elements with a specific binding context. Generally, you get activation for free. However, there are a couple of times where  you may need to activate an element explicitly. In those cases, this is the quickest (and shortest – yes, I’m counting key strokes) way to do that.

I’m not sure if you recall it, but plugins get promoted to Sys helper methods. Take a look at the following code which shows how to use the addHandler plugin to hook up the click event over all the existing buttons:

<head runat="server">
 <script type="text/javascript" 
src="Scripts/MicrosoftAjax/Start.debug.js">
</
script> <script type="text/javascript"> Sys.require([Sys.scripts.ComponentModel], function () { var buttons = document.getElementsByTagName("input"); Sys.addHandler(buttons, "click",function (evt) { alert(evt.target.id); }); }); </script> </head> <body> <input type="button" id="bt1" value="Button 1" /> <input type="button" id="bt2" value="Button 2" /> <input type="button" id="bt3" value="Button 3" /> </body>

Whenever you click over a button,you should get an alert message with the ID of the button. Now, since we’re talking about handlers, here’s how you’d use the addHanders plugin:

Sys.addHandlers(Sys.get("#bt1"),
    {
        click: function (evt) {
            alert("bt-1");
        }
    }
    );     

As you can see, I’ve used a literal JS object for specifying the events I want to handle. You should only use this plugin when you want to pass several handlers to different events. Notice that both plugins (addHandler and addHandlers) expect a reference to one or more DOM elements (you can see how to pass several references in the first snippet; the second passes a single DOM element reference). That’s why you should only use the second (addHandlers) when you need to setup several events at once.

Here’s what you get when you click “Button 1” after adding the last snippet to the page:

addHandlers

Notice that if you add this last snippet to the previous page, you should see two alert messages because you’re effectively adding two handlers to “button 1” click event.

One of the nicest things about plugins is their integration with JQuery. For instance, take a look at the following code:

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
  <script type="text/javascript" 
src="Scripts/MicrosoftAjax/Start.debug.js"></script> <script type="text/javascript"> Sys.require(
[Sys.scripts.jQuery,Sys.scripts.ComponentModel],
function () { $.addHandler( $("input").get(), "click", function (evt) { alert("JQuery: " + evt.target.id); }); } ); </script> </head> <body> <input type="button" id="bt1" value="Button 1" /> <input type="button" id="bt2" value="Button 2" /> <input type="button" id="bt3" value="Button 3" /> </body> </html>

As you can see, I’ve started by requiring JQuery and the MS AJAX JavaScript files needed for using the addHandler method (oh, btw, I could have specified the dependency through the Sys.plugins.addHandler too). After getting all the files, my anonymous callback function will be called and that’s where everything interesting is happening.

There’s an addHandler method which got added to the jQuery object (aliased to the $) during JQuery loading. Notice that the addHandler plugin still expects the same arguments and that’s why we need to use the JQuery’s get method to get a reference to an array with all the DOM elements of the internal wrapped set.

This behavior is pretty cool and also works with other plugins. The only thing you should keep in mind is that non-behavior/control plugins always get added to the $ object; on the other hand, behaviors/controls get added to the $.fn object (and that means that these plugins are “transformed” into “instance” methods of the JQuery object – take a look at this post to see what I mean).

And that’s it”. Stay tuned for more on MS AJAX.

Nov 23

In the last week, MS AJAX beta was released during PDC 2009. One of the things that this released added is ACT script loader support. If you go into the samples, you’ll see that there is an Extended folder which holds all the client files for the behaviors and controls exposed by the AjaxControlToolkit.

extended

The code files have all been updated so that its” definitions are wrapped into an execute function (which is also placed inside an anonymous function). As you probably recall from previous posts, this function is called when all the dependencies have been loaded. Notice that  with the previous CTP you could download the ACT from codeplex and make the changes present in all the files that come with this release. In other words, wrapping all the code within functions is the easiest part when you want to adapt an existing script so that it can be used with the new script loader component.

To me, the most important script file is the ExtendedControl Javascript file because it contains all the definitions (and dependencies) required by all the ACT controls. In practice, this means that you can simply drop this file on a page (plus the base start.js Javascript file) and use any of the ACT controls. Don’t believe me? Here’s some demo code which uses the calendar behavior:

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
 <link rel="stylesheet" type="text/css"
   href=
"Scripts/MicrosoftAjax/Extended/Calendar/Calendar.css" /> <script type="text/javascript" src="Scripts/MicrosoftAjax/Start.debug.js"></script> <script type="text/javascript" src="Scripts/MicrosoftAjax/Extended/ExtendedControls.debug.js"></script> <script type="text/javascript"> Sys.require([Sys.components.calendar], function () { Sys.create.calendar(Sys.get("#date")); }); </script> </head> <body> <input type="text" id="date" /> </body> </html>

And that’s it: I’ve just added two scripts and that’s all I needed to use the CalendarBehavior. Btw, here’s the results I got on my machine after loading the page:

calendar

Nice, right?

Nov 20

You can get it from here. I’m curious to see if it contains new features…

Nov 13

At least, when the RTM version is released!

Ok, I guess you need some context: in the previous post, I talked about a problem which was mentioned to me by Andy in the comments of an existing post. The problem was that the __msajaxBindings field ended up being added to an object used in a live binding and that really means trouble when you try to serialize it (because you end up with a cyclic reference which generates a runtime exception).

In the previous post, I suggested a hack for it. Fortunately, Dave saw it and emailed me assuring that the hack won’t be necessary and that the internal __msajax… fields should only be added to HTML elements. As you’re probably expecting by now, the problem I mentioned in the previous post is the result of a bug (which will be solved before the RTM is out).

After being alerted by Dave, I’ve decided to take a closer look at the code…And yes, there’s a problem and it’s on the Sys.UI.DomElement.isDomElement method. This method ends up invoking the _isDomElement method which has a check that returns a “false positive”:

Sys._isDomElement = function Sys$_isDomElement(obj) {
   var val = false;
   if (typeof (obj.nodeType) !== ''number'') {
     var doc = obj.ownerDocument || obj.document || obj;
     if (doc != obj) {
       var w = doc.defaultView || doc.parentWindow;
       val = (w != obj);
     }
     else {
       val = (typeof (doc.body) === ''undefined'');
     }
  }
  return !val;
}

Can you spot the problem? Yep, the val = (typeof(doc.body)… line is responsible for considering the object a DOM element (remember: section had a property called body and if you look at the code, you’ll see that it ends up messing everything). While the good guys at MS don’t publish a new release which solves this bug, you really shouldn’t use any property named body in an object which is used in a binding relationship (at least,if you intend to serialize that object). Ok,to be fair, you can always use the previous awful hack, but If I were you, I’d renamed the property 🙂

And that’s it for now. Stay tuned for more.

Nov 11

In my “goodbye MS AJAX post”, reader Andy asked a really interesting question: how to serialize an object which is used in a binding relationship? The main problem is that the JavaScriptSerializer doesn’t support circular references. Unfortunately, it doesn’t also provide a way for you to specify which properties should be serialized. However, the problem is still there: how do you serialize the objects used in binding relationships?

I’m still hopping that the team will fix the serializer before MS AJAX RTMs is released, but meanwhile, we’re still left with the problem. I’ve thought a little bit about it and it seems like the best option is to “suspend” the binding, serialize it, and then “resume” it again. Notice that the code you’re about to see has not been tested (it was written during my bus trip home, so it might have one or two bugs 🙂 ,,). Let’s take a look at the example (I’ve reused most of Andy’s example and I’d like you to concentrate on the save method):

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
    <script src="Scripts/MicrosoftAjax/start.debug.js"
        type="text/javascript">
    </script>
    <script type="text/javascript">
        var instance = [
            { title: ''New document'',
                content: {
                    sections: [
                        { body: ''Enter text here'' },
                        { body: ''other text'' }
                    ]
                }
            }
        ];
        Sys.require([Sys.components.dataView],
            function () {
                var view = Sys.create.dataView(
                        "#maincontent",
                        { itemTemplate: ''#edit'',
                          data: instance});
        }); 
function onSave(){ var data = Sys.get("$maincontent").get_data(); var backup = {}; function cleanupMetadata(obj, propName, key) { if (obj.hasOwnProperty(propName)) { backup[key] = {}; backup[key][propName] = obj[propName]; delete obj[propName]; } } function restoreMetadata(obj, propName, key) { if (backup[key] !== undefined) { obj[propName] = backup[key][propName]; delete backup[key]; } } for (var i = 0; i < data.length; i++) { var sections = data[i].content.sections; for (var j = 0; j < sections.length; j++) { cleanupMetadata(sections[j], "_msajaxBindings",
i+
"-"+j); } } var serialized =
Sys.Serialization.JavaScriptSerializer.serialize(data);
for (var i = 0; i < data.length; i++) { var sections = data[i].content.sections; for (var j = 0; j < sections.length; j++) { restoreMetadata(
sections[i],
"_msajaxBindings", i + "-" + j); } } alert(serialized); } </script> </head> <body xmlns:sys="javascript:Sys" xmlns:dataview="javascript:Sys.UI.DataView"> <div id="edit" class="sys-template"> <label>Title</label> <input sys:value="{binding title}"/> <div sys:attach="dataview" dataview:data="{{ content.sections }}" class="sys-template"> <label>Body</label> <textarea>{binding body}</textarea> </div> </div> <div id="maincontent"></div> <button onclick="onSave()">Save</button> </body> </html>

We’re using two helper methods: cleanupMetadata and restoreMetadata. In the cleanupMetadata, we store the _msajaxBinding info in the backup object. As you can see, we’re building a key by combining the positions of the objects so that we can recover that metadata later. Yes, this isn’t really reusable code and I’d really prefer to have the serializer filter the properties which shouldn’t be serialized. However, this approach will probably be enough for now if you need to serialize an object used in a binding relationship.

And that’s it for now…

Nov 09

Well, this really doesn’t deserve any special attention. Ok, let me put it another way: there’s a lot of work going on behind the scenes, but you shouldn’t have to worry with any of it.

I’ve thought about writing a post on the objects that end up being used by the AdoNetDataContext to propagate changes back to the server, but after some thought, I’ve give up on that idea because I’m not seeing anyone use any of these classes directly (yes, the AdoNetDataContext should do it all for you).

And that’s why I’m wrapping up this series on MS AJAX preview 6. I hope you’ve enjoyed it as much as did…Now, I do need to find another topic to keep me busy looking at new stuff…any ideas?

Nov 09

Here we are again, for more on the MS AJAX library. In the previous post, we’ve seen that we can interact with ADO.NET Data Services by using the AdoNetDataContext instances. Today, we’ll be looking at more advanced features of the ADO.NET Data Services which are also available in the MS AJAX library.

Before going on, I must confess that I’m not really an ADO.NET Data Service user (I guess this has made me curious enough for taking a peek in the future). However, since the MS AJAX library offers such great support for it, I couldn’t resist doing some digging and seeing which options are available for interacting with an existing data service.

According to the docs, ADO.NET Data Services can “be surfaced as a REST-style resource collection that is addressable with URIs and that agents can interact with using standard HTTP verbs such as GET, POST, PUT or DELETE”. In practice, and if we go back to our previous example, we can get all the images by using the uri http://:…/Services/ImageDataService.svc/Images. Besides this simple option,which returns all the images contained in the object,there are still other interesting options. For instance:

  • we can navigate through related properties;
  • we can expand associated properties;
  • we can filter the returned results;
  • we can paginate the returned data.

All these operations are specified through uris. We can easily do them by building the uris by hand. For instance, we can modify the previous example so that it also displays the associated :

<body xmlns:sys="javascript:Sys"
      xmlns:dv="javascript:Sys.UI.DataView">
    <div
      id="myView"
      class="sys-template"
      sys:attach="dv"
      dv:autofetch="true"
      dv:dataprovider="{{ _ctx }}"
      dv:fetchoperation="Images"
      dv:fetchparameters="{{ {$expand: ''Tags''} }}">
        <h2>{{Name}}</h2>
        <img sys:src="{{Uri}}" sys:alt="{{Name}}" />
        <div class="sys-template"
            sys:attach="dv"
            dv:data="{{ Tags }}">
            <span>{{Name}}</span>,
        </div>
    </div>
</body>

By using the $expand parameter, we’ll end up getting each image’s associated tags and we can display them by using a nested DataView control. The next image shows what happens when we load the previous page:

expand

We could specify all the “special” parameters this way. However, there is another way: we can rely on the Sys.Data.AdoNetQueryBuilder class for doing building the uri for us.

The AdoNetQueryBuilder’s constructor expects the base uri and exposes several properties which we can use to build the uri which performs a specific operation against the existing ADO.NET Data Service. You’ll probably end up using the ones which are present in the following list:

  • skip and top: we can combine them for paginating data;
  • orderby: used for indicating the sorting order;
  • filter: responsible for filtering the returned data;
  • expand: used for specifying the properties which will be “expanded” and included in the response.

I must confess that I really didn’t find the use of this object intuitive at first. However, after running some experiences, everything started making sense. In order to show you how you might end up using this object, suppose we want to get all the images and associated tags and that we want to order them by name. Here’s a possible solution:

<head runat="server">
    <style type="text/css">
      .sys-template{
        display: none;
      }
      img{
        width:320px;
        height:212px;
      }
    </style>
    <script type="text/javascript"
      src="../Scripts/MicrosoftAjax/start.debug.js">
    </script>
    <script type="text/javascript">
      var _ctx = null;
      Sys.require([Sys.components.adoNetDataContext,
                   Sys.components.dataView],
      function () {
        var uriBuilder = 
new Sys.Data.AdoNetQueryBuilder("Images"); uriBuilder.set_expand("Tags");//$expand uriBuilder.set_orderby("Name");//$orderby _ctx = Sys.create.adoNetDataContext({ serviceUri: "../Services/ImagesDataService.svc" }); Sys.create.dataView( Sys.get("#myView"), { dataProvider: _ctx, autoFetch: true, fetchOperation: uriBuilder.toString() }); }); </script> </head> <body xmlns:sys="javascript:Sys" xmlns:dv="javascript:Sys.UI.DataView"> <div id="myView" class="sys-template"> <h2>{{Name}}</h2> <img sys:src="{{Uri}}" sys:alt="{{Name}}" /> <div class="sys-template" sys:attach="dv" dv:data="{{ Tags }}"> <span>{{Name}}</span>, </div> </div> </body>

This time, I’ve opted for using an imperative approach. Things are really similar to what we had before; however, notice that now we’re using an AdoNetQueryBuilder object to set the fetchOperation property of the DataView object.

We start by instantiating the AdoNetQueryBuilder object. If you recall it, I said that this wasn’t really intuitive…it was only looking at the source code that I’ve understood that I was supposed to pass the name of the resource I wanted to access (instead of passing the base uri for the service – not sure on why the constructor’s only parameter is called uri, when resource would have make things so much easier!). Since I was interested in retrieving images, I only had to pass the string “Images” to the constructor.

After having an instance and setting the required properties (recall from above that we wanted to get images+tags and to order everything by name), we can simply call the toString method to get the string which represents the operation we need to perform. And that’s it. We’ll still be speaking about MS AJAX in future posts, so stay tuned for more!

Nov 09

Getting started with the AdoNetDataContext

Posted in Javascript, MS AJAX       Comments Off on Getting started with the AdoNetDataContext

Today we’re going to start looking at the AdoNetDataContext. This class expands the DataContext object and uses several specific methods for ensuring the correct interaction with an existing ADO.NET Data Service.

These internal methods added by the AdoNetDataContext are essentially used in identity related operations. In other words, it extends the base DataContext class by introducing several methods which turn on identity support (btw, I’m talking about the stuff we’ve seen in several of the previous posts).

In this post, I’ll be reusing the sample ADO.NET Data Service which is included in MS AJAX preview 6. Here’s the model used internally by the ImageDataService.svc ADO.NET service:

adonetmodel

In this case, and since this is just an intro post, we’ll build a simple page which shows all the images returned by this ADO.NET service:

<head runat="server">
    <style type="text/css">
      .sys-template{
        display: none;
      }
      img{
        width:320px;
        height:212px;
      }
    </style>
    <script type="text/javascript"
      src="../Scripts/MicrosoftAjax/start.debug.js">
    </script>
    <script type="text/javascript">
      var _ctx = null;
      Sys.require([Sys.components.adoNetDataContext,
                   Sys.components.dataView],
      function () {
        _ctx = Sys.create.adoNetDataContext({
            serviceUri: "../Services/ImagesDataService.svc",
            mergeOption: Sys.Data.MergeOption.appendOnly
        });
    });
    </script>
</head>
<body xmlns:sys="javascript:Sys"
      xmlns:dv="javascript:Sys.UI.DataView">
    <div
      id="myView"
      class="sys-template"
      sys:attach="dv"
      dv:autofetch="true"
      dv:dataprovider="{{ _ctx }}"
      dv:fetchoperation="Images">
        <h2>{{Name}}</h2>
        <img sys:src="{{Uri}}" sys:alt="{{Name}}" />
    </div>
</body>

By now, you shouldn’t really be surprised by anything that is shown in the previous snippet. As you can see, the AdoNetDataContext class protects us from all those nasty details associated with the communication with the ImageDataService.svc server side service. I’m not going to show you how to add, edit or remove items because the samples released with preview 6 already have code which shows how to do those things.

On the next post,we’ll see how to do more interesting operations by using the AdoNetQueryBuilder object. Stay tuned for more on MS AJAX.

Nov 08

The DataContext component: creating entities

Posted in Javascript, MS AJAX       Comments Off on The DataContext component: creating entities

Before we can move on and take a look at the AdoNetDataContext component, we need to talk about one more topic: creating entities and how it might be integrated with DataContext component. The DataContext exposes a createEntity method which can be used for  creating new objects of a specific type. Internally, that method will always delegate the creation of a new entity to the function to which the createEntityMethod property points to.

As you’re probably wondering, this is something you’ll only need to configure if you need to use the DataContext features associated with identity (and even in that case, setting the createEntityMethod is optional if you’ve set the getNewIdentityMethod property).

The createEntityMethod property should reference a function which receives two parameters (a DataContext reference and a string with the “type” of the object that should be created) and initializes the metadata necessary for tracking that object. If you recall the previous posts, you’ll surely remember that we’ve added an internal __meta property to all the objects stored by the DataContext instance.

We could improve our DataContext used at the time by setting the createEntityMethod to a function which initializes the metadata associated to that object. Here’s how I’d augment the entityManager we’ve been using with a new method used for creating new entities:

createNewEntity: function (dataContext, entitySet) {
    var obj = {};
    entityManager.getNewIdentity(dataContext, obj, entitySet);
    return obj;
}

As you can see, we’re simply relying in the getNewIdentity method because it is responsible for setting the __meta object used for that purpose. Referencing entityManager instead of this might seem little strange at first, but that’s needed because the DataContext won’t call the reference through the entityManager instance (it will simply call the function through the reference it got to it during initialization:

var ctx = Sys.create.dataContext(
  {
      serviceUri: "PeopleService.svc",
      getIdentityMethod: entityManager.getEntityId,getNewIdentityMethod: entityManager.getNewIdentity,isDeferredPropertyMethod: entityManager.isDeferredProperty,
      getDeferredPropertyFetchOperationMethod: 
entityManager.getDeferredProperty, createEntityMethod: entityManager.createNewEntity } );

And that’s it. There’s really not much to say about the createEntity method. Stay tuned for more on MS AJAX.

Nov 07

The DataContext component: deferred properties

Posted in Javascript, MS AJAX       Comments Off on The DataContext component: deferred properties

Today we’ll keep looking at the DataContext component and we’ll see how it supports deferred properties. Deferred properties are properties which are lazy loaded as “needed”. Using deferred properties means that the component will try to make a remote call for getting the value of that property. As you’re probably expecting, this means that you’ll have to write code on the client and server side. On the client, we’ll need some JavaScript functions which are responsible for:

  • checking if a specific property is deferred;
  • returning an instance of the Sys.NetWebServiceOperation which has all the data needed for performing the remote call which returns the value of that property.

After creating the functions and configuring the DataContext component’s isDeferredPropertyMethod and getDeferredPropertyFetchOperationMethod , we can get the deferred value of the property by using the DataContext’s fetchDeferredProperty method. Let’s take a look at the client side code (which augments the code we introduced in previous posts):

var entityManager = {
    metaCounter: {},
    getEntityId: function (dataContext, entity) {
       //removed: check previous posts
    },
    getNewIdentity: function (dataContext, entity, entitySet) {
        //removed: check previous posts
    },
    isDeferredProperty: function (dataContext, entity, propertyName) {
        var id = dataContext.getIdentity(entity);
        return propertyName === "contacts" &&
               id !== null &&
               /$new/.test(id);
    },getDeferredProperty: function (
dataContext,entity, propName, parameters, userContext) {
//returns web service operation var id = dataContext.getIdentity(entity); return new Sys.Net.WebServiceOperation( "GetContactsFor", {id: parseInt(/(d+)/.exec(id)[0]) }, "get" ) ; } };

It goes without saying (I think!) that this is just demo code (you’ll need better code for real world scenarios). As you can see, the contacts property will only be lazily loaded when we’re trying to get it from an object which has been previously persisted in the server side (once again, you’ll need to go back to the previous posts to understand what’s going on here). In the real world, I’d probably check that property against undefined too so that you’d only ask for the data when the property hasn’t been initialized.

The getDeferredProperty tries to find the id of the current person’s instance (don’t forget that person instances which have been previously persisted are on the form people$xxx where xxx is an integer which identifies the current instance) and uses it to build a JS literal object which is used to pass parameters to the server side code. Notice that the Sys.Net.WebService’s “constructor” expects three parameters: the first identifies the name of the operation; the second is a literal object which represent the parameters that will be passed to the server side method. Finally, the third parameter is a string which identifies the HTTP method that should be used in the server side call.

Now, we’re ready to create a new DataContext instance:

var ctx = Sys.create.dataContext(
  {
    serviceUri: "PeopleService.svc",
    getIdentityMethod: entityManager.getEntityId,
    getNewIdentityMethod: entityManager.getNewIdentity,
    isDeferredPropertyMethod: entityManager.isDeferredProperty,
    getDeferredPropertyFetchOperationMethod: 
entityManager.getDeferredProperty } );

One additional note before looking at the server side code: we *really* need to set the serviceUri property when we intend to use deferred property fetching. If you don’t set it, you’ll end up receiving an exception when you try to get the value of the property from the server side:

ctx.fetchDeferredProperty(
    people[0],
    "contacts",
    null,
    null,
    function(){
        var contacts = people[0].contacts;
       //contacts already filled here!
    });

The fetchDeferredProperty method receives several parameters:

  • a reference to the entity whose property is going to be obtained through the remote call;
  • name of the property which is being lazily fetched;
  • parameter: literal object with parameter info that will be passed to the server side. Note that this object will be merged with the parameter object encapsulated by the WebServiceOperation returned from the DataContext’s getDeferredPropertyFetchOperationMethod function;
  • mergeOptions: the Sys.Data.MergeOption which identifies the merge which will be used when the data is returned from the web service;
  • succeededCalback: callback function which will be invoked when the data is returned from the server and has already been passed to the property;
  • failedCallback: function invoked when the remote call ends up generating an error;
  • timeout: timeout defined in ms;
  • userContext: object which will be passed to the callback method when the DataContext receives a response from the server side.

In order for the previous client code work, we need to update our server side service’s code by adding the GetContactsFor method. Once again, and since is just demo code, I’ll implement in the easiest way possible: I’ll make it return a collection with a single predefined contact:

[OperationContract]
public IEnumerable<Contact> GetContactsFor(Int32 id) {
    return new List<Contact>{
        new Contact{
                Type = ContactType.Phone,
                Ct  = "123123123"
        }
    };
}

And there you go: you don’t need anything else for making the DataContext component get the value of the contacts property lazily. And that’s it for now. Stay tuned for more on MS AJAX.

Nov 05

The DataContext component: linking objects

Posted in Javascript, MS AJAX       Comments Off on The DataContext component: linking objects

Today we’ll keep going in our study of the DataContext component and we’ll see how we can establish relationships between objects through links. I guess you’re probably asking something like: “Hum…establishing relationships between objects…how about using properties?” For instance, suppose we’re working with person and contact instances from the previous post…We can say that a person has one contact and that is easily expressed through JavaScript which looks like this:

var personWithContact = {
  id: 6,
  name: "dumb",
  contact: { value: 1, contact: "123123123" }
};

Now, this is perfectly good code…really…so, why would I want to use links then? Glad you asked :,,)

Before going on, I’d like to point out that this is a feature which was added in order to support ADO.NET Data Services. Fortunately, the team wrote it in a generic way so that you could use it even if you’re not using ADO.NET Data Services (I said fortunately because you can reuse this feature if you want). But I’m divagating…why would we use links? For bookkeeping, of course! Whenever you create a link between two objects, you end up setting a property in the source and you’ll also get an entry in the changes array (internal _changelist array).

When working with links, you’ve got several methods which you can use:

  • addLink: you should use this method when you need to add items to a collection. For instance, suppose you’ve got a property which is an array and you want to add an object to that array. The addLink method is the way to go;
  • setLink: also used for adding a new link between two objects. This is the method you should use when you’re thinking of “simple” properties (instead of collections);
  • removeLink: used for deleting an existing link between two objects. You should only use this method when you’re removing an item from a collection

As I’ve said above, you should keep in mind that these methods end up changing the internal changes list maintained by the DataContext control. Establishing and cancelling links produces different results when you’re working with single properties and collections. For instance, take a look at the following code (based in code presented in the previous post):

var newPerson = { name: "joseph", address: "fx", 
contacts: [] };
var ct1 = { value: 1, contact: "123123123" }; var ct2 = { value: 2, contact: "222333444" }; ctx.addLink(newPerson, "contacts", ct1);//collection ctx.setLink(newPerson, "contact", ct2);//single prop ctx.setLink(newPerson, "contact", null);//removing single ctx.removeLink(newPerson, "contacts", ct1);//removing coll.

In the previous snippet, we’ve introduced a new person instance and several contacts in order to show you how to establish links between the objects. If you stop the code in the addLink method, you’ll see something like this:

addlink

As you can see, establishing the link through the add method ended up adding the contact to the contacts array of the person object and inserting a new entry in the changes list. If you stop the code’s execution after the setLink line, you’ll see something similar (depending on what changes were made to the DataContext):

addlink2

As you can see, the main difference is that setLink ends up setting an item instead of adding it to a collection. Canceling a relationship established through the setLink method involves using that method again, but passing null instead of a valid value. On the other hand, removing an item from the collection should always be done with the removeLink method. Interestingly, these methods produce different results in this scenario (don’t forget that we still haven’t committed the changes, ie, we still haven’t committed our setLink and addLink changes). Take a look at the following picture to see what I mean:

addlink3

As you can see, the removeLink call ended up deleting the previous addLink entry (while also removing the entry from the contacts array). However, the setLink method doesn’t affect the changes array in the same way; instead, it will simply update the existing entry so that it points to the new target that has been passed to it. Notice that this means that you’ll have to pay attention to this if you’re writing custom code in the server side which is responsible for persisting the changes.

And I guess this sums it up quite nicely. but there’s still more to discuss…so stay tuned for more on MS AJAX.

Nov 04

In the previous post, we’ve started looking at identity and how it’s supported by the DataContext component. Today we’ll improve the initial algorithm so that we’re able to support more advanced scenarios.

Even though the first algorithm for identity management was really simple, it ended up showing that identity management ends up adding (at least) two important problems:

  • how to generate new identities;
  • how to distinguish new entities from existing ones and how to distinguish different types of entities.

One possible solution for these problems relies on adding some sort of metadata to our objects. Going back to our initial example, we’ll start by adding an internal __meta object which will hold two important pieces of data:

  • the type of object;
  • the identity of that object;

Notice that the identity of the object must be enough for identifying that object (even amongst objects of different “types”). Another important detail to keep in mind is that we need some way to identify the current type of an object (if we were using a strongly typed language, this wouldn’t be necessary, but with dynamic languages, it’s something that must also be tracked). It’s also important to understand that the objects that will be passed to the trackData method should have this info correctly filled up (unlike objects which will be inserted, where the metadata is supposed to be generated by the identity methods):

var people = [
    { id: 1, name: "luis", address: "fx", 
__meta: { set:
"people", id: "people$1"} },{ id: 2,name: "john", address: "fx",
__meta: { set:
"people", id: "people$2"} }, { id: 3, name: "peter", address: "fx",
__meta: { set:
"people", id: "people$3"} } ];

There’s not much we can say about the previous snippet. It represents a collection of person instances which have their __meta properties set up. Notice that from now on, the identity of each of these objects is given by the __meta.id value (instead of relying in the id field of the object).

Our implementation of the identity related methods need to handle more details. Due to that, I’ve decided to create a helper object which encapsulates all the work needed for getting and creating new identities:

var entityManager = {
  metaCounter: {},
  getEntityId: function (dataContext, entity) {
    return entity.__meta && entity.__meta.id ?
           entity.__meta.id :
           null;
  },
  getNewIdentity: function (dataContext, entity, entitySet) {
    if (!entitySet) {
      Error.invalidOperation("entity set must be defined");
    }
    var _this =entityManager;
    function createMetaInfo(entity, entitySet) {
      var generatedId = _this.metaCounter[entitySet] =
        _this.metaCounter[entitySet] !== undefined ?
            _this.metaCounter[entitySet] + 1 : 0;
      return {
        set: entitySet,
        id: entitySet + "$new:" + generatedId
      };
    }
    if (!(entity.__meta && _entity.__meta.id)) {
      entity.__meta = createMetaInfo(entity, entitySet);
    }
    return entity.__meta.id;
  }
};

A couple of observations regarding the previous helper:

  • the identity of an item relies on the __meta.id property. As you can see, identity relies on concatenating IDs with the type of the current object;
  • new identities must be different from those that belong to objects which have already been persisted (probably in a db on the server side). That’s why new identity use the $new instead of using only $ (I think this is easy to handle on the server side);
  • I’ve introduced the metaCounter object so that we can reuse this object for allowing the insertion of different types of objects.

Now, we can start adding several kinds of objects without any problem. For instance, take a look at the following code:

ctx.insertEntity({ name: "joseph", address: "fx" }, 
"people"); ctx.insertEntity({ name: "joseph", address: "fx" },
"people"); ctx.insertEntity({ value: 1, contact: "123123123" },
"contacts");

And the next image shows the final result:

betteralgorithm

As you can see, with this approach we no longer set the id property of a new person object (that will be done by the server side, when it process an eventual commit performed by the DataContext instance).

Notice that until now we’ve seen the client code needed for supporting addition of new items. A complete solution would still need more work in the server side in order to ensure that items are correctly persisted. On the next post, we’ll start looking at links between objects. Stay tuned for more on MS AJAX.

Nov 03

The DataContext component: getting started with identities

Posted in Javascript, MS AJAX       Comments Off on The DataContext component: getting started with identities

We’ve already met several interesting features of the DataContext component. Today we’ll keep going and we’ll start discussing identity. I’m not sure if you’ve noticed, but the DataContext exposes several identity related methods. For instance, the getIdentity method is used by the control to get the identity of an object.

In order for you to activate identity related features, you need to (at least) configure the component so that it knows how to get or create a new identity for an object. This is done by setting two properties:

  • getIdentityMethod: function used for getting the identity of an object. This method expects two parameters: the first references the DataContext; the second, references the entity you’re interested in getting the ID of;
  • getNewIdentityMethod: function which is used for creating a new identity for an object. This method expects three parameters: the first two match the getIdentityMethod’s parameters; the last one is an optional string which identifies the type of object that is asking for a new identity.

Setting these properties is all that is need for activating identity support for a DataContext component. Notice that using identities means  that you’ll end up using the internal _items object for storing all the objects maintained in the current DataContext component. This object is used as a dictionary, where the identity of each object is used as the key of a specific entry. In practice, this means that you can mix object of different types in the same _items dictionary (we’ll talk about this in a future post).

As you’ve probably guessed, identity is one of those features which exists (mainly) for supporting ADO.NET data service. However, nothing prevents us from exploring it, right? So let’s get starting…and we’ll start simple…Suppose you want to track changes to the array presented in the next snippet:

var people = [
    { id: 1, name: "luis", address: "fx" },{ id: 2,name: "john", address: "fx" },
    { id: 3, name: "peter", address: "fx" }
  ];

In this case, things are pretty simple, right? We’re only interested in managing simple objects (lets call them person instances) and that means that we can use the id value as the identity of each object. There’s still one little problem: what to do with new items?

In this case, and since we’re supposed to create a function for calculating new identities, we can write it so that it also sets the id of any new person object. Here’s the code needed for instantiating a DataContext component which does exactly that:

var ctx = Sys.create.dataContext(
  {
    getIdentityMethod: function (dataContext, entity) {
      return entity.id;
    },
    getNewIdentityMethod: function (
dataContext, entity, entitySet) {
if (entity.id === undefined) { var contextItems = ctx.get_items(); var max = 0; for (var id in contextItems) { if (parseInt(id) > max) { max = parseInt(id); } } entity.id = max + 1; } return entity.id; } } );
ctx.trackData(people);

With these properties sets, we can add new objects without having to worry with the ID of a new person instance:

ctx.insertEntity({ name: "joseph", address: "fx" });

The next image shows the changes made to the DataContext instance after the previous insert:

identitychanges

As you can see, identity activation leads to several changes:

  • new elements get added to the private _items dictionary with the new identity used as the key;
  • the new inserted object is updated: a new id property is added and set to the new identity which was calculated by the DataContext’s newIdentityMethod function;
  • the _inserts array references only the id of the item that was inserted (notice the id$4 entry in the previous image).

The example we’ve seen is really intended to get you started with identities. Unfortunately, things aren’t so easy in the real world. there will be times where you can’t really set the ID of an item (ex.: suppose it gets mapped to an auto number field of a table). There will also be other times where you need to support several “types” of objects…in these scenarios, you’ll need a more advanced way to identifying each instance.

In the next post we’ll take a look at more advanced ways of generating IDs for these scenarios. Stay tuned for more on MS AJAX.

Nov 02

The DataContext component: Committing changes – take II

Posted in Javascript, MS AJAX       Comments Off on The DataContext component: Committing changes – take II

In the previous post, we’ve seen how easy it is to commit the changes that are buffered by a DataContext instance. At the time, I said that there were still a couple of things related with the saveChanges method that needed further discussion…and that’s what we’ll do in this post.

To illustrate one scenario you might end up facing in the real world, lets assume that our person instances have a piece of information which can only be set in the server side. For instance, lets say that all created instances need to have a creation date that can only be set in the server side. Going back to our initial example, that means that each person instance in the initial items array needs to be updated with this extra info (keep in mind that items is supposed to have info about previously saved elements):

var items = [
    { name: "luis", address: "fx", date: new Date() },
    { name: "john", address: "fx", date: new Date() },
    { name: "peter",address: "fx",date: new Date() }
  ];

To make the code more real, we’ll be adding a DataView control which allows us to edit/add new items to this array. Take a look at the following image:

commit1

The code used for generating this page looks like this:

<head runat="server">
    <style type="text/css">
        .sys-template{
            display: none;
        }
    </style>
    <script type="text/javascript"
      src="Scripts/MicrosoftAjax/start.debug.js">
    </script>
    <script type="text/javascript">
        var items = [
          { name: "luis", address: "fx", date: new Date() },
          { name: "john", address: "fx", date: new Date() },
          { name: "peter", address: "fx", date: new Date() }
        ];
        var aux = { name: "charles", address: "lx" };
        var ctx = null;
        Sys.require([Sys.components.dataContext,
                     Sys.components.dataView],
            function () {
              Sys.Observer.makeObservable(items);
              ctx = Sys.create.dataContext({
                serviceUri: "PeopleService.svc",
                saveOperation: "SavePeople",
                saveHttpVerb: "POST"
              });
              ctx.trackData(items);
              $addHandler(
                      Sys.get("#add"),
                      "click",
                      function () {
                       var item = { name: "", address: "" };
                       ctx.insertEntity(item);
                       items.add(item);
                      });
              $addHandler(
                    Sys.get("#save"),
                    "click",
                    function () {
                      ctx.saveChanges();
                    });
            });
    </script>
</head>
<body
      xmlns:sys="javascript:Sys"
      xmlns:dv="javascript:Sys.UI.DataView">
    <div id="view"
         class="sys-template"
         sys:attach="dv"
         dv:data="{{items}}">
      <div>
        <input type="text" sys:value="{binding name}" />
        <input type="text" sys:value="{binding address}" />
        <span>{binding date}</span>
      </div>
    </div>
    <input type="button" value="add new" id="add" />
    <input type="button" value="save changes" id="save" />
</body>

As you can see, the items array is used in two places: it feeds the data shown by the DataView control and it is tracked by the DataContext component. We’ve transformed the items array into an observable array so that the DataView is able to automatically pick up insertions and deletions.

Even though this works, there’s still a small problem here: the date of each new person instance can only be set in the server side. The previous web service didn’t return anything from the server side and that is why you’ll always get null for the date property.

If you look at the code of the DataContext component, you’ll notice that it checks for values that are returned from the server for a save operation. Take a look at the _processResults method (edited for improved readability):

function Sys$Data$DataContext$_processResults(
dataContext, changes, results) {
if (results && results.length === changes.length) { dataContext._ignoreChange = true; try { for (var i = 0, l = results.length; i < l; i++) { var result = results[i],
change = changes[i],
item = change.item;
if (result && typeof(result) === "object") { dataContext._fixAfterSave(
change, item, result); } } }
finally { dataContext._ignoreChange = false; } } }

As you can see, if the web service method used for saving returns a collection, the component will try to merge the client information with the one returned from the server side. It’s imperative that the returned collection has the same size as the array of changes (_changelist property) that was sent to the server and that each returned item’s position matches the “correct” position in the changes array. With this info, we can easily solve our initial problem by updating the server side save method:

[OperationContract]
public IEnumerable<Person> SavePeople(
IEnumerable<PersonOperation> changeSet) { var changed = new List<Person>(); foreach (var change in changeSet) { change.Person.Date = DateTime.Now;//bad!!!demo only _executors[change.OperationType](change.Person); changed.Add(change.Person); } return changed; }

We’ve updated the web service save method so that it returns the collection of person instances it received from the client. Notice that in this case I’ve forced an update of the Date property and that info will be propagated back to the client and picked up by the internal _processResults method (you shouldn’t really do this for all items – after all, we only need to set the date for insertions). And that’s why you’ll get dates after you click the save changes button:

commit2

And I guess that’s all for now. Stay tuned for more on MS AJAX.

Nov 02

Ok, we’ve already seen one option for fetching data from a remote web service. In this post, we’ll see how to commit changes tracked by the DataContext component. Committing buffered changes means that you’ll need to set (at least) two DataContext instance properties: serviceUri and saveOperation. serviceUri identifies the URI of the web service that is responsible for persisting the changes; saveOperation is used for identifying the server side method which persists the changes that were buffered by the DataContext instance.

To illustrate committing, we’ll be updating the PeopleService we’ve introduced in a previous post. For now, we’ll start by writing some JavaScript code:

var items = [
    { name: "luis", address: "fx" },
    { name: "john", address: "fx" },
    { name: "peter", address: "fx" }
  ];
  Sys.require([Sys.components.dataContext],
    function () {
      var ctx = Sys.create.dataContext({
        serviceUri: "PeopleService.svc",
        saveOperation: "SavePeople",saveHttpVerb: "POST"
      });
      ctx.trackData(items);
      var aux = { name: "charles",address: "lx" };
      ctx.insertEntity(aux);
      ctx.saveChanges();
    });

As you can see, we’re configuring three properties for our DataContext object:

  • serviceUri: we’re using this to pass the URI to the web service that is going to be used for saving the changes;
  • saveOperation: identifies the name of the method exposed by the web service that should be used for saving the changes;
  • saveHttpVerb: we’re setting this property to specify the HTTP verb that should be used when the DataContext tries to commit the changes previously buffered.

Btw, notice that we’re using the trackData method for initiating data tracking. I’ve opted for doing this to show you that saving does not depend on fetching data from the same web service. You should also keep in mind that the saveChanges method might receive three parameters:

  • the first, identifies the success callback function;
  • you can also pass an error callback function (second parameter);
  • finally, you can pass additional custom context that will be passed for the callback methods you’ve passed as first and second parameters.

Before showing the server side code, we need to know what is passed to the server side when someone calls the saveChanges method. The answer shouldn’t be too hard (at least, after thinking a little bit about it): we’ve already seen that the DataContext component saves all changes by adding entries to the internal _changelist field. And that is the information which gets send back to the server when the component tries to persist the buffered changes.

With this information, we can start updating the server side code. The first thing we need to do is create  a new C# class which represents an instance of a change. In other words, we need to mimic the Sys.Data.ChangeOperation in the server side. Now, if I was building a reusable class, I’d use templates and I’d duplicate all the properties exposed by the ChangeOperation type.

However, since this is demo code and I know that the buffered operations will only involve insertions, editions and removals of Person objects (after all, we still haven’t talked about links), I’ve opted for only replicating the properties that are used in this scenario:

[DataContract]
public class PersonOperation
{
    [DataMember(Name="item")]
    public Person Person{ get; set; }
    [DataMember(Name="action")]
    public OperationType OperationType { get; set; }
}
public enum OperationType
{
    Insert,
    Update,
    Remove
}

As you can see, we’ve added a new enum too. That was necessary because the client side ChangeOperation instance relies in using an enum for specifying the operation that is being performed (the only thing we need to ensure is that the enum entries have the same values as the ones specified in the client side).

The PersonOperation class mimics the Sys.Data.ChangeOperation “type” we have in the client side. As you can see, we’re using the DataMemberAttribute for mapping each of the server properties to the ones used in the client side. Oh, and just for completion, here’s the code for the Person class:

[DataContract]
public class Person {
    [DataMember(Name="name")]
    public String Name { get; set; }
    [DataMember(Name="address")]
    public String Address { get; set; }
}

Now, we’re still missing the web service’s save method. We’ve already established that it receives a collection of changes (ie, an array of Sys.Data.ChangeOperation objects). However, there’s still one important piece of information missing: we need to know the name of the parameter used for sending that information. In this case, the DataContext object uses the changeSet name. And now we’re read for taking a peek at the web service code:

[ServiceContract(Namespace = "")]
[AspNetCompatibilityRequirements(
RequirementsMode =
AspNetCompatibilityRequirementsMode.Allowed)] public class PeopleService { static List<Person> _people = new List<Person> { new Person{ Name = "luis", Address = "fx"}, new Person{ Name = "john", Address = "lx"}, new Person{ Name = "peter", Address = "pt"} }; private readonly
IDictionary<OperationType, Action<Person>> _executors; public PeopleService() { _executors =
new Dictionary<OperationType, Action<Person>> { {OperationType.Insert, InsertItem}, {OperationType.Update, UpdateItem}, {OperationType.Remove, DeleteItem} }; } [OperationContract] [WebGet] public IEnumerable<Person> GetPeople(SortOrder sort){ var ordered = _people.OrderBy(
p => (sort == SortOrder.Name ? p.Name : p.Address) ); return ordered; } [OperationContract] public void SavePeople(
IEnumerable<PersonOperation> changeSet) { foreach (var change in changeSet) { _executors[change.OperationType](change.Person); } } private void InsertItem(Person person) { //do something } private void UpdateItem(Person person) { //update person } private void DeleteItem(Person person) { //delete item } }

We rely on the OperationType property of each PersonOperation instance to decide if we’re inserting, updating or removing an item (I’ve opted for using a dictionary for eliminating the switch that you’ll see in most cases). It’s just that simple, really! The only thing to watch out for are the mappings: you need to ensure proper mapping between the client and side objects (and this includes method parameter names too!).

This has become a rather large post, so I guess this is it for now. There are still a couple of interesting observations that can be made about the server side save method, but we’ll leave it for a future post. Stay tuned for more!

Nov 02

Until now, we’ve met some of the basic features introduced by the DataContext component. As I’ve said, you can think of this control as a buffer which keeps track of changes performed to several items it keeps track of. We’ve already seen that we can use the trackData method for initiating a tracking operation. What I haven’t mentioned yet is that you can also configure the control so that it fetches data automatically from a web service (or even from an ADO.NET data service).

Getting data from a web service means that you’ll have to (at a minimum!)configure the URI of the web service. Currently, you can do that by setting the DataContext’s serviceUri property. After setting the URI, you can get the data by calling the fetchData method. Here’s an example (btw, I’m reusing the web service presented here):

<script type="text/javascript">
  Sys.require([Sys.components.dataContext],
    function () {
      var ctx = Sys.create.dataContext(
        {
          serviceUri: "PeopleService.svc"
        }
      );
      ctx.fetchData(
        "GetPeople", //method
        {sort: 0 }, //method parameters
        Sys.Data.MergeOption.overwriteChanges, //merge options
        "GET", //http verb
        function () { //succeded
          //could use parameters, but wil use
          //lastFetchedResults for demo purpose
          var items = ctx.get_lastFetchDataResults();
          for (var i = 0; i < items.length; i++) {
            alert(items[i].name);
          }
        });
    });
</script>

After setting the serviceUri property,we end up calling the fetchData method (while passing it several parameters which influence the remote web service call) for getting the data from the web service. As you can see,we’re only passing the succeeded callback function to ensure that the lastFetchDataResults property is correctly filled up (in this case, we’re just iterating over each item and printing the value of its name property).

The fetchData exposed by the DataContext component matches the one defined by the IDataProvider interface. This isn’t just coincidence… it happens because the DataContext implements this interface and needs to provide an implementation for its fetchData method:

Sys.Data.IDataProvider.prototype = {
    fetchData: Sys$Data$IDataProvider$fetchData
}

Btw, here’s the current signature for the fetchData method:

function Sys$Data$IDataProvider$fetchData(operation, 
parameters,
mergeOption,
httpVerb,
succeededCallback,
failedCallback,
timeout,
userContext) {

I guess that the parameters are self-explanatory, so I won’t be wasting your time with additional details on them. If you’ve been following along, then you’re probably read one of the posts on how to configure the DataView control to fetch data from a remote web service.

At the time, I’ve said that one of the options is configuring the DataView’s dataProvider property with a valida IDataProvider instance. Since the DataContext implements this interface, then you can also use an object of this type for getting the data that is rendered by the DataView control. Here’s a quick example that shows how to configure the DataView control so that it gets the data from a DataContext:

<head runat="server">
    <style type="text/css">
        .sys-template{
            display: none;
        }
    </style>
    <script type="text/javascript"
      src="Scripts/MicrosoftAjax/start.debug.js">
    </script>
<script type="text/javascript">
  Sys.require([Sys.components.dataContext,
               Sys.components.dataView],
    function () {
      var ctx = Sys.create.dataContext({
        serviceUri: "PeopleService.svc"
      });
      var view = Sys.create.dataView(
        Sys.get("#view"),
        { itemTemplate: "#template",
          autoFetch: true,
          dataProvider: ctx,
          httpVerb: "GET",
          fetchOperation: "GetPeople",
          fetchParameters: {
            sort: Sys.Data.MergeOption.overwriteChanges
          }
        });
    });
</script>
</head>
<body>
    <div id="view">
    </div>
    <div class="sys-template"
        id="template">
        <span>{{name}}</span>-
        <span>{{address}}</span>
        <br />
    </div>
</body>

The code is pretty straightforward (I think): we create a new DataContext instance and set its serviceUri property. After that, we initialize a DataView control and pass it the remaining info that is needed to perform the remote call. And that’s it. There’s nothing more to it than that…

The next step in your study guide is see how to configure the DataContext so that it propagates changes from the client to the server side. Stay tuned for more!

Nov 02

The DataContext class: cancelling changes

Posted in Javascript, MS AJAX       Comments Off on The DataContext class: cancelling changes

In the previous post, we’ve see how to execute some basic actions over a DataContext instance. In this post we’ll keep looking at the it and we’ll see the available options for working with changes made over the data tracked by the control. Before getting started, we’ll assume that _ctx references an existing DataContext object created previously with the code shown in the previous post.

The first thing you can do is cancel all the changes that have been made to the data encapsulated by the control. You can easily do this by calling the clearChanges method. This method ends up doing several things:

  • it starts by cleaning the internal arrays used for keeping track of the items that were inserted, edited and removed (ie, it sets the _inserts_edits and _deletes objects to null);
  • it clears the private _changelist array (by using the Sys.Observer.clear method);
  • it sets the _hasChanges property to false and generates the propertyChanged event.

Here’s some demo code that shows how to use this method:

var aux = { name: "charles", address: "lx" };
_ctx.insertEntity(aux);
Sys.Observer.setValue(aux, "address", "pt");
alert(_ctx.get_changes().length); //at least 1 change here!
_ctx.clearChanges();
alert(_ctx.get_changes().length);//no changes here

As you can see, inserting a new entity ensures that the _changelist is not empty. After calling the clearChanges method, all pending changes are deleted. Even though pending changes are cancelled (ie, cleared), you still have access to the initial data that was tracked by the control (that is,if the control encapsulated any data that was being tracked).

If you also need to clear the tracked data,then you’ll need to resort to the clearData method. Besides clearing pending changes, this method will also clear the internal arrays it uses (including _items and _lastResults) ensuring proper clean up of the internal state of the control. Here’s some demo code that shows how to use this method:

_ctx.clearData();
alert(_ctx.get_lastFetchDataResults() === null);//true!

As you can see, calling the clearData method means that you’ll end up “nulling” the private _getchanges field of the DataContext instance.

Besides cancelling changes, you can also “commit” changes. But before we go into this topic, we need to talk about interacting with remote web services. And that’s what we’ll start doing in the next post. Stay tuned for more.

Oct 30

Getting started with the DataContext object

Posted in Javascript, MS AJAX       Comments Off on Getting started with the DataContext object

Yesterday, I’ve started looking at the DataContext object. I haven’t still tested all its features (to do that, I still need to take a look at ADO.NET Data Services and study the AdoNetDataContext type), but I’ve already picked up some ideas about it.

I guess it’s fair to say that you can think of the DataContext as a buffer which keeps track of the changes  you do along the time. Besides tracking basic changes, it does also support several more advanced concepts like links (which are just “fancy relations” between different objects – I think that most of this stuff is there to support interactions with the ADO.NET Data Services).

Since this is just an intro post, we’ll only be looking at its basic features. Creating a new DataContext instance can be easily accomplished through the Sys.create object helper:

<script type="text/javascript">
  var items = [
    { name: "luis", address: "fx" },
    { name: "john", address: "fx" },
    { name: "peter", address: "fx" }
  ];
  var _ctx = null;
  Sys.require([Sys.components.dataContext])
  Sys.onReady(function () {
    _ctx = Sys.create.dataContext();
});
</script>

Once again, we rely on the script loader for getting the necessary JavaScript files and populating the helper objects with new methods (ex.: Sys.create.dataContext). The previous DataContext instance is “empty”. However,I’m positive that in most scenarios you’re really interested in populating it with some data that will be tracked over time. The DataContext lets you do that through trackData method:

_ctx.trackData(items);

If you’ve looked at the DataContext’s prototype object,then you might think that calling trackData ends up initializing the items property. However, that will only happen if the DataContext object has been previously configured with identity related methods (we’ll be talking about this feature in future posts). Since we’re not using these advanced features, we’ll end up getting the data saved in the _lastResults property:

datacontext1

Now that the DataContext has been “initialized” with data, we can use it to, say, add a new item to that internal array:

_ctx.insertEntity({ name: "charles", address: "lx" });

You won’t be surprised by knowing that the previous line ends up “adding” a new item to the list of objects tracked by the _ctx DataContext instance. However, the new item won’t be added to the _lastResults array; instead, it gets added to the private _inserts property:

datacontext2

I’ve expanded the _lastResults, _items and _inserts fields of the _ctx instance. As you can see, _lastResults remains unchanged. The inserted item is appended to the _inserts array and you’ll also get a new entry on the _changeslist property. Even though I’m mentioning the _lastResults and _inserts fields, the truth is that you shouldn’t use them in your code (they’re considered private details and should only be managed by the DataContext instance). You can, however, get a reference to the _changeslist field through the get_changes public property:

var changes = _ctx.get_changes();

The local variable changes is just a collection of Sys.Data.ChangeOperation instances, which expose the following properties:

  • action: value from the Sys.Data.ChangeOperation enumeration (insert, update or remove) which identifies the current entry;
  • item: reference to the item of the operation (when adding, editing or removing items from the data context, this property references the object which was used in the operation);
  • linkSource: used for identifying the source of a link operation;
  • linkSourceField: identifies the field of the linkSource object used in the link operation;
  • linkTarget: identifies the target object used in a link operation.

The last three properties displayed in the previous list are only used for link operations (which we’ll talk about in future posts).

Since changes is an array, we can print all the changes by using a simple for loop. Notice that this will only show changes which haven’t been “persisted” by the DataContext instance (more about this topic at the end of this post):

for (var i = 0; i < changes.length; i++) {
  alert(changes[i].action + "-" +
                changes[i].item.name + "-" +
                changes[i].linkSource + "-" +
                changes[i].linkTarget + "-" +
                changes[i].linkSourceField);
}

To wrap up the data context’s insertEntity method topic, I’d like to add two things:

  • currently, the method expects two parameters: the first, is a reference to the entity that you’re adding to the current context; the second, is supposed to be a string which identifies the entity set to which this element belongs (this will be used when the DataContext instance has been customized to support identities – since we’re not using those features, we didn’t pass anything for this second parameter);
  • all the insertion operations need to go through the insertEntity method… and this is also true for observable arrays (in other words, if you’re thinking that you can add new items to an observable array which has been associated with a DataContext through the use the “observable array methods” and still have those changes recorded by the DataContext, then forget it).

Now that we’ve seen how insertion works, it’s time to see how we can remove items from the DataContext instance. In this case, we must use the removeEntity method:

_ctx.removeEntity(items[0]);

Removing an item ends up adding a new ChangeOperation to the internal _changelist field. If you run the previous for loop, you’ll see that it has two changes: one for the insertion and another one for the removal of the item. And yes, the private _deletes array will end up with one entry (which references the element that has been removed).

Now, lets make things a little more interesting…Take a look at the following snippet:

var aux = { name: "charles", address: "lx" };
_ctx.insertEntity(aux);
_ctx.removeEntity(aux);

What do you expect to happen now? If you’re thinking that you’ll end up with _insert and _delete empty arrays and with no changes (ie, with an empty _changeslist array), then you’re absolutely right. That happens because you’ve inserted and removed the same item without “committing” the insertion. The next image shows these results:

datacontext3

From the three basic operations, we’re only missing one: editing an item. To understand editing, we need to go back and talk a little more about tracking data. Besides initializing the _lastResults field, the trackData ends up hooking all the array items’ property changed events so that the DataContext instance is notified of any change made to any of the objects that it’s tracking.

In practice, this means that you’ll need to use the observable methods to change an item that is being tracked by a DataContext instance. For instance, the next snippet changes the address of the first item that is being tracked:

Sys.Observer.setValue(items[0], "address", "pt");

Which produces the following internal changes in the DataContext instance:

datacontext4

Btw, do notice that in editing scenarios, the item ends up being modified (notice the _lastResult’s first item address). As you might expect, the edit operation ended up leading to new entries in the private _changelist and _edits fields too.

Notice that if you’re updating an item which was added after the DataContext has started tracking data, then that edit operation will be performed, but it won’t be registered in the _changeslist collection (because there’s still a pending insert operation which, when performed in the back office, ends up doing everything  with a single insertion instruction).

And I guess this covers the basic operations related with inserting, editing and removing items that are being tracked by a DataContext object. In the next posts, we’ll keep looking at this object. Stay tuned for more in MS AJAX.

Oct 29

DataViews, DataViews and more DataViews: DataViews everywhere!

Posted in Javascript, MS AJAX       Comments Off on DataViews, DataViews and more DataViews: DataViews everywhere!

Ok, I think this title might have caught your attention! (at least, that’s what I’m hopping for!). This post is a direct consequence of another discussion with a friend regarding the use of the DataView control. He told me something like this: “well, DataViews are cool…can’t deny it…but it’s only for listing items and that means it’s only usable for grids…”

Aha! That is not correct. And to show him that he was wrong, we’ve settled in a challenge (not as interesting as top gear’s challenges, but still…). After some discussion, we’ve settle in simple scenario: an edit form for a complex JavaScript object. Here’s the UI:

complexform

The print button you see is there to give feedback on the values of the properties of the person object. And here’s the JavaScript for the objects used in the sample:

var contactTypes = [
      { id: 1, description: "phone" },
      { id: 2, description: "email"}];
var person = {
  name: "luis",
  address: "some place",
  contacts: [
    { contact: "123123123",type: contactTypes[0]},{ contact: "labreu@somewhere.pt", type: contactTypes[1]}
  ]
};

As you can see, we’ve got a collection of literal contact types objects which are used for identifying the types of each of the contacts saved in the person object.  To solve this problem, I’ve ended up defining three DataView controls:

  • one for the top form;
  • another for listing the contacts;
  • another one for populating the SELECT control used for presenting the type of contact (we can all agree that this is not a grid, right?:) ).

Here’s the final markup I’ve ended up using:

<div id="mainView"
     class="sys-template"
     sys:attach="dv"
     dv:data="{{person}}">
     <label for="name">Name:</label>
     <input type="text" name="name" 
sys:value="{binding name}" /> <br /> <label for="name">Address:</label> <input type="text" name="name"
sys:value="{binding address}" /> <table> <thead> <tr> <td>Contact</td> <td>type</td> </tr> </thead> <tbody sys:attach="dv" class="sys-template" dv:data="{binding contacts}"> <tr> <td> <input type="text"
sys:value="{binding contact}" /> </td> <td> <select id="contactTypes" sys:attach="dv" class="sys-template" sys:value=
"{binding type, convert=getId, convertBack= getContactType}" dv:data="{{ contactTypes }}"> <option sys:value="{{id}}">
{{description}}
</option> </select> </td> </tr> </tbody> </table> </div> <input type="button" value="print info" id="print" />

There are a couple of interesting things going on here:

  • the top DataView control is used for allowing me to use templates (which rely on bindings) for filling the form automatically and setting the context for the inner DataView controls;
  • the second DataView control displays a grid which shows the contacts for the current person. As you can see, it’s really similar to what” we’ve used before.
  • the third DataView is applied…to a SELECT element. We need to fill the select with the available contact type options and the easiest way to do that is to simply associate it with a DataView control and set its data to the contactTypes object. However, we need to select the correct type for the “current” contact and that’s where the sys:value enters the scene.

Btw, we do need to define two convert functions for ensuring proper setting of each of objects that participate in the binding relationship. Here’s the code for those functions:

Sys.converters.getId = function (value, binding) {
  return value.id;
};
Sys.converters.getContactType = function (val, binding) {
  for (var i = 0; i < contactTypes.length; i++) {
    if (contactTypes[i].id == val) {
      return contactTypes[i];
    }
  }
  //probably should throw here?
  return null;
};

The getId conversion function is invoked when the value is being propagated from the person’s contact object to the dropdown. In this case, we only need the ID in order to select the correct OPTION; on the other hand, when the value is being propagated from the dropdown to the “current” contact, we do need to convert the OPTION’s value into an object (and that’s why we’re using a for loop in the getContactType method). Btw, here’s another picture which shows that alterations are in fact propagated back to the object:

complex2

Even though I’m not showing the code for print button, I can assure you that it’s the longest method of this demo page. As you can see, the DataView is really a useful control. Combining it with templates and bindings does really cut down the code needed for several real world scenarios.

And that’s it for now. Stay tuned for more!

Oct 29

DataView and remote web service invocation: adapting the data returned from the server

Posted in Javascript, MS AJAX       Comments Off on DataView and remote web service invocation: adapting the data returned from the server

Yesterday I’ve ended up receiving an interesting question regarding the code I’ve used in the posts which explain how to get data from a remote web service. The question was: how do we adapt the data returned from the web service before it is used by the DataView for rendering its HTML?

This is an interesting question. Fortunately, the answer is not really complicated. To illustrate this problem and its solution, we’ll start by going back to  the Person class we’ve introduced in the server side:

[DataContract]
public class Person {
    [DataMember(Name="name")]
    public String Name { get; set; }
    [DataMember(Name="address")]
    public String Address { get; set; }
}

As you can see, we’re using the DataMemberAttribute for influencing the name of the properties used in the JavaScript objects that will be returned from the service. Now, let’s assume that we don’t own the service and that the Person class looks like this:

[DataContract]
public class Person {
    [DataMember()]
    public String Name { get; set; }
    [DataMember()]
    public String Address { get; set; }
}

Now the problem: we want to adapt the data received from the server so that we don’t have to change the template used by the view. Btw, here’s how the template is currently defined:

 <tr>
    <td>{{name}}</td>
    <td>{{address}}</td>
  </tr>

If we leave the code as is, we’ll end up getting errors because *now* each JavaScript Person instance has two properties named Name and Address (instead of name and address). Ok, I guess you could say that it’s a lot easier to just update the names of the properties used in the templates than to change the data; however, changing the data gives me the chance to introduce another technique which might be useful for you in other less contrived scenarios…

When I thought about intercepting and changing the data, I thought that the fetchSucceeded event would be a good place to do that. Unfortunately, this event is only fired after the DataView control has refreshed itself: by then, you’d already received an exception saying that name is undefined!

Ok,time to go back to the prototype definition of the control…and to read a previous post on the topic of DataView events. And yes,the answer to our problem is right there, on the first event described on this post:

rendering: this event is generated when the DataView control is about to start instantiating its template to get the rendered HTML. Any function which handles this event receives two parameters: the first, references the DataView control which generated the event; the second is an instance of the Sys.Data.DataEventArgs type;

Hooking up the rendering event is all we need to adapt the data. here’s some code that adapts the JavaScript objects returned by the server (as you can see, we’re replacing each instance with a new one that has the correct names):

function updateData(sender, e) {
  var data = e.get_data();
  for (var i = 0; i < data.length; i++) {
    data[i] = { name: data[i].Name, 
address: data[i].Address } } }

Since we’re using a declarative approach, hooking up the event can be done by using the on rendering attribute:

dv:onrendering="{{ updateData }}"

And that’s it for now. Keep tuned for more on MS AJAX.

Oct 28

In the previous posts, we’ve seen how to configure the DataView control so that it gets the data from a remote web service. In this post, we’ll see how we can take advantage of the fetchParameters to pass info to the web service. The idea is simple: we’ll update the previous sample so that it renders a table with clickable headers used for sorting the displayed data. Sorting the data will be done on the server. This means we need to start updating the server side code so that the web service method gets the info used for sorting (notice I’m only showing the changes made to the initial code):

public class PeopleService {
    static List<Person> _people = new List<Person>
        {
            new Person{ Name = "luis", Address = "fx"},
            new Person{ Name = "john", Address = "lx"},
            new Person{ Name = "peter", Address = "pt"}
        };
    [OperationContract]
    [WebGet]
    public IEnumerable<Person> GetPeople(SortOrder sort) {
        var ordered = _people.OrderBy( 
p => (sort == SortOrder.Name ?
p.Name : p.Address) ); return ordered; } } public enum SortOrder { Name, Address }

As you can see, changes are really minimal: we’ve added an enumeration used for indicating the sort order that should be applied to the collection and we’ve updated the GetPeople method so that it orders the collection accordingly. Now, let’s see the client code:

<head runat="server">
  <title></title>
  <style type="text/css">
      .sys-template{
          display: none;
      }
  </style>
  <script src="Scripts/MicrosoftAjax/start.debug.js" 
type="text/javascript">
</
script> <script type="text/javascript"> Sys.require([Sys.scripts.jQuery, Sys.scripts.WebServices,Sys.components.dataView],function () { Sys.loadScripts(
[
"PeopleService.svc/jsdebug"], function () { Sys.Application.activateElement(
Sys.get("#view")); $("thead td").click(function () { var view = Sys.get("$view"); view.get_fetchParameters()
.
sort =
(
$(this).text() === "Name" ?
SortOrder.Name :
SortOrder.Address); view.fetchData(); }) }); }); </script> </head> <body xmlns:sys="javascript:Sys" xmlns:dv="javascript:Sys.UI.DataView"> <table> <thead> <tr> <td>Name</td> <td>Address</td> </tr> </thead> <tbody id="view" class="sys-template" sys:attach="dv" dv:autofetch="true" dv:httpverb="GET" dv:dataprovider="{{ PeopleService }}" dv:fetchoperation="GetPeople" dv:fetchparameters="{{ { sort: SortOrder.Name } }}"> <tr> <td>{{name}}</td> <td>{{address}}</td> </tr> </tbody> </table> </body>

 

Most of the code is similar to what we had before. There are, however, some differences:

  • I’ve added JQuery to set a click handler to all the TD elements of the header (I still love jQuery 🙂 );
  • we’ve attached the DataView control to the tbody element of the table. This is the easiest way to define the templates without ending up with crappy HTML (thanks go once again to Dave Reed for pointing me in the right direction);
  • in response to a click event on one of the header TDs, we set the sort parameter to the correct value. In a real world app, I’d check that the user was really asking for a different sorting before starting the remote call which refreshes the control (after all, if the data is already ordered by Name, there really isn’t any need to reorder it again by name, right?);
  • after setting everything up, we refresh the data presented by the control by calling the fetchData method. This method will perform the remote call and will automatically refresh the DataView control with the new sorted data.

And there you go: quick and easy, right? Stay tuned for more on MS AJAX.

Oct 28

Feeding the DataView control with data returned from a Web Service – take II

Posted in Javascript, MS AJAX       Comments Off on Feeding the DataView control with data returned from a Web Service – take II

In the previous post, we’ve seen how we can configure the DataView control to get data returned from a web service. The previous approach is a good one if you don’t intend to use a proxy to a web service. However, suppose you’ve already have a web service proxy and that you want to reuse it for getting the data that is needed by the DataView…in that case, the best option might be reusing that proxy.

To show you the necessary steps for configuring the DataView control to do that, we’ll reuse the previous server side code and we’ll only update the client side:

<head runat="server">
  <title></title>
  <style type="text/css">
      .sys-template{
          display: none;
      }
  </style>
  <script src="Scripts/MicrosoftAjax/start.debug.js" 
type="text/javascript">
</
script> <script type="text/javascript"> Sys.require([Sys.scripts.WebServices, Sys.components.dataView], function () { Sys.loadScripts(["PeopleService.svc/jsdebug"], function () { Sys.Application.activateElement(
Sys.get("#view")); }); }); </script> </head> <body xmlns:sys="javascript:Sys" xmlns:dv="javascript:Sys.UI.DataView"> <div id="view" class="sys-template" sys:attach="dv" dv:autofetch="true" dv:httpverb="GET" dv:dataprovider="{{ PeopleService }}" dv:fetchoperation="GetPeople"> <div> <span>{{name}}</span>- <span>{{address}}</span> </div> </div> </body>

Ok, so there’s lot of things going on here:

  • We start by indicating the required dependencies (similar to what we had in the previous post). Since we want to use a web service proxy, we need to add a new script node which references the “special” url PeopleService.svc/jsdebug (notice that I’ve got the page and the service in the same folder). However, we can only do that after the base classes used by the proxy have been defined and injected in the page (that’s why we’ve passed an anonymous function which will be called when all the base dependencies are loaded);
  • nothing prevents you from adding the script nodes “by hand”. However, you should keep in mind that the Sys.loadScripts method exists and contains all the code needed for adding the new script node and hooking up the ready event so that you get notified when the script has been loaded;
  • unfortunately,loading a new script from within the first callback function (the one that was passed for the Sys.require method) stops future processing of the normal notifications of the page. In other words,you will not get any Sys.onReady or pageLoad calls. And that’s why we need to activate the DataView control “by hand”. Fortunately, we can rely on a simple method call for kicking off that work (notice the Sys.Application.activateElement call). Btw, if you had several controls, you could easily activate them all by passing a reference to the document element (document.documentElement). And yes, we’re activating the pages from within a callback that will get called adter the web service proxy code has been loaded and processed;
  • the only noticeable thing in the markup is the DataView’s dataProvider definition: we used a one-time/one-way binding to set it to the web service proxy which was added to the page through the previous Sys.loadScripts call.

And I guess this sums it up quite nicely. But there’s still lots of things to talk about so stay tuned for more on MS AJAX.

Oct 28

Until now, we’ve been using static data to feed all the DataView controls we’ve been using in the MS AJAX series. Today we’ll see how we can configure the control so that it automatically gets data returned from a web service call. To get things started, we’ll start by introducing the server side code for the WCF service:

[ServiceContract(Namespace = "")]
[AspNetCompatibilityRequirements(
RequirementsMode =
AspNetCompatibilityRequirementsMode.Allowed)] public class PeopleService { [OperationContract] [WebGet] public IEnumerable<Person> GetPeople() { var people = new List<Person> { new Person{ Name = "luis", Address = "fx"}, new Person{ Name = "john", Address = "lx"}, new Person{ Name = "peter", Address = "pt"} }; return people; } } [DataContract] public class Person { [DataMember(Name="name")] public String Name { get; set; } [DataMember(Name="address")] public String Address { get; set; } }

Yes, not the best code in the world, but don’t forget that I’m not interested in creating web service! What I want is to show you how to properly configure the DataView control to get data from a web service…

As you can see, PeopleService is a WCF service which has a single operation: GetPeople. This method returns a collection of Person instances (since I wanted to preserve JS naming conventions, I’ve used the DataMemberAttribute to customize the name of the properties used in JSON returned from the server).

And now we can proceed with the most important part: configuring the DataView for getting the data automatically from the server side. In this case,I’ve opted for using the declarative approach:

<head runat="server">
    <title></title>
  <script src="Scripts/MicrosoftAjax/start.debug.js" 
type="text/javascript">
</
script> <script type="text/javascript"> Sys.require([Sys.scripts.WebService,
Sys.components.dataView]); </script> </head> <body xmlns:sys="javascript:Sys" xmlns:dv="javascript:Sys.UI.DataView"> <div id="view" class="sys-template" sys:attach="dv" dv:autofetch="true" dv:httpverb="GET" dv:dataprovider="PeopleService.svc" dv:fetchoperation="GetPeople"> <div> <span>{{name}}</span>- <span>{{address}}</span> </div> </div>

We start by loading the JS files needed for communicating with the server and using the DataView control. Since we need to communicate with the server side through a remote web service method call, we need to get the base classes defined in the MicrosoftAjaxWebService.js file.

Configuring the DataView isn’t really hard since we only need to set a couple of properties:

  • autofetch: this property expects a Boolean. Setting it to true makes the DataView control fetch the data from the web service automatically during its initialization;
  • httpverb: you can use this property to set the type of HTTP method call which will be used for communicating with the server;
  • dataprovider: this property is used for setting the provider which will return the info that populates the DataView control. You can pass one of the following types to it: a reference to an object which implements the Sys.Data.IDataProvider interface (ex.: a DataContext object), a reference to a web service proxy or a string with the URI of the service. In this example, I’ve opted for using a string which identifies the URI of the web service (we’ll leave the other options for future posts);
  • fetchOperation: the name of the operation which returns the data. You’ll typically use this property when you pass a web service proxy to the DataView’s dataProvider property. In the previous snippet, I’ve opted for using it to set the name of the method (notice that this wasn’t really needed because I could have initialized the DataView’s dataProvider property with the complete path to the web service method – ie, “PeopleService.svc/GetPeople”).

Besides these properties, there are a still a couple of properties related with getting data from a remote provider:

  • fetchParameters: literal object with the parameters that should be passed for the remote service method;
  • timeout: you can use this property to set the timeout in milliseconds;
  • isFetching: read-only property which indicates if the control is performing a remote call for fetching the data;

When the DataView control is fetching data from a remote provider, you can abort that operation by invoking the abortFetch method. On the other hand, you can force a remote call by invoking the fetchData method. Calling this method starts a remote call and will automatically refresh the control with the new data that is returned from the provider.

Besides properties and methods, the control has a couple of events which you can handle:

  • fetchFailed: this event will be generated when the remote call fails;
  • fetchSucceeded: as you can probably guess, this event will be fired when the DataView control receives a successful response from the provider.

A fetchfailed handler receives three parameters:

  • a reference to the error returned from the server;
  • a custom user context object which might have been passed to the control;
  • a string (“fetchData”) which identifies the method that originated the handle call;

A fetchSucceeded handler also expects the same number of parameters as the ones that are passed to the fetchFailed handler: the main difference is that the first parameter references the data returned from the service.

And this should be enough for getting you started with getting data through remote calls. There’s still more to come, so stay tuned!

Oct 27

In these last posts, we’ve already met several of the events generated by the DataView control. I thought it would be a good idea to write a small post which documents many of the events we’ve seen in the past:

command: fired when a user clicks over an element which has been “marked” with the sys:command system attribute;

  • rendering: this event is generated when the DataView control is about to start instantiating its template to get the rendered HTML. Any function which handles this event receives two parameters: the first, references the DataView control which generated the event; the second is an instance of the Sys.Data.DataEventArgs type.;
  • rendered: marks the end of the HTML rendering process. Functions configured as handlers receive two parameters: the first, references the DataView which generated the event; the second, is an instance of the Sys.DataDataEventArgs type;
  • itemRendering: event that is generated before each item’s template instantiation. We can do several interesting things here, like changing the default template or influencing the data that is going to be used during the template instantiation;
  • itemRendered: event which signals the end of the instantiation of a template. We’ve seen how we can use this event to customize the generated HTML for an item.

The Sys.Data.DataEventArgs object used by the rendering and rendered events introduces some interesting properties:

  • data: references the items that will be used for instantiating the templates;
  • itemPlaceholder: used for identifying the place holder where the template’s generated HTML will be put;
  • itemTemplate: reference to the template used for instantiating all the elements.

During the rendering event, you can set all of these properties  and influence the HTML generated by the DataView control.

Besides these events, there are still others, but we’ll leave them for the next posts because they’re related with the integration of the DataView control with data contexts. Stay tuned for more on MS AJAX.

Oct 26

In one of the previous posts of the series, we’ve seen how easy it is to build a master-detail scenario with the help of the new DataView control. Today, we’ll keep looking at it and we’ll see how we can build an editable grid. The next two images explain the objective of our post. The first shows the grid in browse mode while the second illustrates what it looks like in edit mode:

browsemode

editmode

As you can see, the idea is simple: any row can be in editing or browsing mode (notice that we can only edit one row at the time). While in browsing mode, you can move it to edit mode or you can simply delete it. In edit mode, you can only confirm the changes or cancel them.

If you’ve been playing with the DataView control, then you’ll know that you’ll need two templates: one used for displaying the row in browse mode and another to display it in edit mode. Here’s the HTML I’ve used for the control and for each of the templates:

<body xmlns:sys="javascript:Sys"
      xmlns:dv="javascript:Sys.UI.DataView">
    <div id="view"
          sys:attach="dv"
          dv:data="{{items}}"
          dv:onitemrendering="{{handleItemRendering}}"
          dv:itemtemplate="#browse"
          dv:oncommand="{{handleCommand}}">
    </div>
    <div id="browse"
          class="sys-template">
        <div>
          <span>{{name}}</span> -
          <span>{{address}}</span>
          <a sys:command="edit" href="" 
sys:commandargument="{{$index}}"
onclick="return false;">Edit</a> <a sys:command="delete" href=""
sys:commandargument="{{$index}}"
onclick="return false;">Delete</a> </div> </div> <div id="edit" class="sys-template"> <div> <input type="text" id="name"
sys:value="{binding name, mode = oneWay}"/> <input type="text" id="address"
sys:value="{binding address, mode = oneWay}"/> <a sys:command="update"
sys:commandargument="{{$index}}" href=""
onclick="return false">Update</a> <a sys:command="cancel" href=""
onclick="return false">Cancel</a> </div> </div> </body>

In this case, I’ve opted for using two external templates: the first one uses spans for showing the name and address of each object + the anchors needed for raising edit and delete commands. The second one has some textboxes plus update and cancel “buttons”. By default, the DataView control uses the browsing template (notice the dv:itemtemplate=”#browse” attribute usage).

As you can see, we’re using commands for signaling the operations. We’re also relying on command arguments for getting the current position of the element that raises a command (the only command which doesn’t require knowing the position is the cancel command).

One thing that might have caught your attention is the use of one-way bindings between the INPUT controls and the objects. Unfortunately,that’s the only way to guarantee that the changes made in the textboxes won’t be propagated automatically to the associated objects (in other words,two way bindings won’t help here because they will automatically propagate all the changes to the associated objects and that means the cancel button wouldn’t really work).

Without further ado, lets take a look at the JavaScript:

<script type="text/javascript">
  var items = [
    { name: "luis", address: "fx" },
    { name: "john", address: "lx" },
    { name: "rita", address: "pt" }
  ];
  Sys.require([Sys.components.dataView],
  function () {
Sys.Observer.makeObservable(items); Sys.UI.DataView.prototype.editElement =
f
unction (pos, addingNewItem) { this.__editIndex = pos; this._isNewItem = addingNewItem; } Sys.UI.DataView.prototype.cancelEditElement =
fun
ction () { this.__editIndex = -1; this.__isNewItem = false; } }); function handleCommand(sender, e) { function delItem(sender, e) { items.removeAt(e.get_commandArgument()); } function updateItem(sender, e) { var name = sender.get_contexts()[ e.get_commandArgument()] .get("#name") .value; var address = sender.get_contexts()[ e.get_commandArgument()] .get("#address") .value; //probably do some validation here var item = items[e.get_commandArgument()]; item.name = name; item.address = address;
sender.cancelEditElement(); sender.refresh(); } function editItem(sender, e) { sender.editElement(e.get_commandArgument(), false); sender.refresh(); } function cancelItem(sender, e) { sender.cancelEditElement(); sender.refresh(); } switch (e.get_commandName()) { case "delete": delItem(sender, e); break; case "update": updateItem(sender, e); break; case "cancel": cancelItem(sender, e); break; case "edit": editItem(sender, e); break; default: return; } } function handleItemRendering(sender, e) { function find(item, arr) { for (var i = 0; i < arr.length; i++) { if (arr[i] === item) { return i; } } return -1; } if (sender.__editIndex === undefined || sender.__editIndex === -1 || find(e.get_dataItem(), items) !==
sen
der.__editIndex)
{
return; } e.set_itemTemplate("#edit"); } </script>

 

There are a couple of interesting things going on here:

  • we start by adding two new helper methods to the DataView’s prototype object: editElement and cancelEditElement. The first is used for setting the __editIndex property with the index of the row that should be placed into edit mode; the second method cancels an ongoing editing operation (by setting that property to –1);
  • most of the work happens in the handleCommand function. As  you can see, we’ve got a separated inner function for each command. This was not needed, but I guess it improves reading quite a bit;
  • the edit anchor doesn’t really do much: it will simply set the current edit index to the value it receives from the command argument;
  • cancelling is even simpler since it simply resets the current edit index;
  • removing an item is also simple because we’ve made items an observable array (and that means easy elimination and propagation of that info to the DataView control);
  • updating is the most complicated operation. We start by getting the values of the textboxes (by relying on the get helper method which is available from the current TemplateContext object). After that, we should probably run some sort of validation to ensure that the introduced values are correct. We’ve dismissed that step because this is only demo code. Since the textboxes weren’t bound to the object’s property, we need to update those properties “by hand”. That means we’ll need to use the command argument value to get the current index in order to get a reference to the item that is being edited;
  • several of the operations end up firing a refresh operation. This refresh is needed to ensure that the correct template is used;
  • We need to handle the itemRendering event because we might need to change the default template. When the “current item” is in edit mode, we change the template through the set_itemTemplate helper (as you can see, we needed to write some extra code because currently we can’t get the position of the current item from the event args parameter that is passed to the function).

And that’s it. With a little JS code, we’ve ended with a cool template which lets us add edit in place to a simple grid. We could improve the previous code for supporting new items, but I’ll leave that for you. And that’s it for now. Stay tuned for more on MS AJAX.

Oct 26

One of the cool features MS AJAX preview 6 introduces is better JQuery integration. From now on, MS AJAX components (which have been defined through a script info object registered through a defineScript(s) method call) are exposed as jQuery plugins. This means that you can write something like this:

<head runat="server">
  <style type="text/css">
      .sys-template{
          display:none;
      }
  </style>
  <script src="Scripts/MicrosoftAjax/start.debug.js" 
type="text/javascript">
</
script> <script type="text/javascript"> Sys.require([Sys.components.dataView,
Sys.scripts.jQuery]); var items = [ { name: "luis", address: "fx" }, { name: "john", address: "lx" }, { name: "rita", address: "fx" } ]; Sys.onReady(function () { $("#myDv").dataView({
data: items,
itemTemplate: "#template" }) .css("background-color", "lightgray"); }); </script> </head> <body> <div id="myDv"> </div> <div id="template" class="sys-template"> <div>{{name}}</div> </div> </body>

Pretty cool, right? Instead of using the traditional MS AJAX Sys.create helper methods, we’re using a JQuery object for wrapping the div so that we can attach it to a MS AJAX DataView control and set its background color.

Notice that the dataView method (added to the jQuery.fn object) is built on the fly when the JQuery script is listed on the depedencies list passed to the Sys.require method. This is one of those small details that makes life better for everyone…Stay tuned for more.