New LightSwitch HTML Client APIs (Stephen Provine)

New LightSwitch HTML Client APIs (Stephen Provine)

Rate This
  • Comments 28

Following the recent announcement of the LightSwitch HTML Client Preview 2, we are excited to introduce a set of additional APIs that allow further control over the behavior and capabilities of a LightSwitch HTML-based application. You can download Preview 2 from the Developer Center.

The APIs we included as part of the June Preview primarily covered UI customization through custom rendering of controls in the DOM, post-rendering (i.e. tweaking) of existing DOM elements, and a data binding API to wire the UI up to data. For Preview 2, we have significantly improved the design-time coding experience with better IntelliSense support and debugging of the original source code, introduced a number of additional coding entry points for responding to common events, and built a richer set of APIs for interacting with the application.

Intellisense Support

We know that writing JavaScript code can be hard when you don’t know the shapes of all the objects you are working with. IntelliSense is one mechanism that can be used to help guide developers to discover and use new APIs. In the June Preview, we offered some existing IntelliSense support for the jQuery library but had minimal support for the LightSwitch library and application-specific assets such as entities or screens.

This all changes with Preview 2. Whether navigating the contents of the LightSwitch library object (msls) or understanding the properties on generated entity or screen objects, IntelliSense now offers completion lists with appropriate glyphs and tooltip descriptions for each item. Here are some examples:

Intellisense1

Intellisense2

We still have work to do in this area to ensure IntelliSense always works in all the expected places, such as in callback functions passed to promise objects, but it has been much improved since the June Preview. Please try it out and let us know what you think!

The Entity and Screen created Events

It is a very common scenario to configure defaults for new entities and screens. The June Preview provided no way to plug in code at the appropriate time to implement this. Preview 2 introduces the created event, allowing you to write code immediately after a new entity or screen has been created.

Suppose you have created a new HTML client project and designed an Order entity in the Entity Designer as follows:

Created1

Now, switch the perspective of the entity from “Server” to “Client” using the buttons at the bottom of the designer:

Created2

Having different entity perspectives is a new feature with Preview 2 and allows you to independently configure aspects of how an entity appears on the server versus a client. One of these aspects is code. Specifically, it allows you to write JavaScript-based entity code that can run in the browser instead of using C# or VB which can only execute on the server.

Now in the designer toolbar, locate the “Write Code” dropdown, open it, and click the “created” link. This will open a code file and output the event stub into which you can add some defaulting logic: 

myapp.Order.created = function (entity) {
  
// Write code here.
  entity.OrderDate = new Date();
};

In this case, a new Order on this HTML Client will set its OrderDate property to the current date.

To try this out, let’s first configure the summary property for the entity to show the OrderDate. This is located in the properties window in the entity designer:

Created4

With this done, right click on the Client project, choose “Add Screen…” and add a browse screen for orders, then do the same for an add/edit details screen for orders. Return to the browse screen and add a button that adds and edits a new order entity:

Method1

Created5

If you run this application and click the “Add Order” button, it will launch the add/edit details screen and the order date will be set to the current date.

Custom Screen Methods

While the June Preview enabled a lot of flexibility regarding the user experience via the render and postRender events, it provided no built-in capability to add buttons to your UI that call custom code to perform specific business tasks. Preview 2 introduces custom screen methods that are designed in a similar manner to screen methods in Silverlight. For each content item that represents a button, you can write canExecute code, which determines the visible or enabled state of the button, and execute code, which actually performs the work.

Continuing the example from the previous section, let’s add a custom button that will import some orders from the sample Northwind OData service:

Method2

Method3

If you right click either the screen method or the content item representing the button in the tree, you can choose to “Edit Execute Code”. This takes you to the code editor and produces the necessary stub, into which we can add the necessary code:

myapp.BrowseOrders.ImportOrders_execute = function (screen) {
 
// Write code here.
  var northwind =
"http://services.odata.org/Northwind/Northwind.svc";
 
return msls.promiseOperation(function (operation) {
    OData.read({ requestUri: northwind +
"/Orders?$top=10"
                 recognizeDates:
true,
                 enableJsonpCallback:
true },
                
function success(result) {
                  
var results = result.results;
                  
for (var i = 0, len = results.length; i < len; i++) {
                     var importedOrder = screen.Orders.addNew();
                     importedOrder.OrderDate = results[i].OrderDate;
                   } 
 
                   operation.complete();
                 },
                
function error(e) { operation.error(e); }); 
    });
};

Since calling an OData service is an asynchronous operation and is potentially long running, this code uses the msls.promiseOperation() function to create a LightSwitch-compatible promise object that represents the operation of calling the OData service and processing the results. This promise is then returned to the LightSwitch runtime. The runtime uses this promise to track the operation, and if it appears to be taking a long time, a progress indicator is shown to the user, blocking additional operations from running and interfering with the current operation.

LightSwitch uses OData to communicate with the middle tier, so all HTML clients already include the datajs client library which provides the OData object for reading and updating data through an OData service. This code simply reuses that API to request data from the external Northwind OData service, then on success, adds the imported orders into the screen’s collection of orders and sets the order date for each one.

Now to illustrate some simple canExecute code, let’s ensure that the user can only perform a single import per instance of the browse screen. First, add a data item to the screen of type Boolean called “ImportPerformed”. Then right click either the screen method or the content item in the tree and this time choose to “Edit CanExecute Code”:

myapp.BrowseOrders.ImportOrders_canExecute = function (screen) {
   // Write code here.
 
return !screen.ImportPerformed;
};

Finally, add a line to the execute code that sets this property to true right before completing the operation:

importedOrder.OrderDate = results[i].OrderDate; } screen.ImportPerformed = true; operation.complete(); },

When the button is initially shown, the canExecute code is called and it returns true:

Method7

When the import operation has completed, the runtime automatically detects that the ImportPerformed property changed and calls the canExecute code again, and this time it returns false, which greys out the button:

Method8

Other Events

The events that have been described so far in this post are considered primary coding entry points for a LightSwitch application. These have end to end support in the designer and are static events, meaning they apply to all instances of asset to which they are attached. For example, the entity created entry point applies to all newly created entities, or a screen method execute entry point applies to all instances of the screen in the running application.

In addition to these static events, there are also instance-based events that can be hooked up from static event handlers. These events are exposed using more traditional JavaScript event patterns, which include both single (obj.onevent) and multi-handler syntaxes (obj.addEventListener()).

The most common instance event in LightSwitch is the “change” event. In fact, since it is so common, we introduced a helper API for attaching to change events – addChangeListener() – that uses addEventListener() under the covers. Since the HTML client does not currently support imperative validation, let’s expand on the Order example and see if we can use a change listener to provide some simple custom validation of the order date.

Open the add/edit details screen for the Order and write the following code in the screen created entry point:

myapp.OrderDetail.created = function (screen) { 
  // Write code here.
 
screen.Order.addChangeListener("OrderDate", function (e) {
   
var order = screen.Order, contentItem = screen.findContentItem("OrderDate"),
    today =
new Date();
    today.setHours(0, 0, 0, 0);
   
if (order.details.entityState === msls.EntityState.added && 
        contentItem.validationResults.length === 0 && 
        order.OrderDate < today) {
          contentItem.validationResults = [
            
new msls.ValidationResult( order.details.properties.OrderDate,
                                       
"Cannot be before today.") ];
    }
  });
};

This code is initially invoked when the OrderDetail screen is created, but it subsequently attaches a listener that is called each time the OrderDate property of the Order for the screen changes. When this happens, it finds the content item that is backing the date picker control showing the order date in the UI, then sets the validation results if a) the entity is in a pending add state, b) does not already have validation errors, and c) the date does not occur before today’s date.

With this code, if you run the application and add a new order, then change the order date to a date before today, you will see the desired validation error attached to the control:

Other1

If you change it to a date in the future, the validation error disappears:

Other2

The code does not explicitly remove the existing validation error, but when the value changes the runtime clears out the current validation results and revalidates the data, so it works as desired in this case.

This example is not perfect (it does not include code to unhook from the change event when leaving the screen) but gives a sense for what can be achieved using these instance events. The change event is present on almost every LightSwitch object: entities, data services, screens, and many other objects accessible from these core objects.

The Application Object

As with the existing Silverlight client, a LightSwitch HTML client application combines a LightSwitch-provided shell with user-provided screens and data. The HTML client shell handles not only navigation between screens, tabs and dialogs but also the commit model for the application. These gestures are available through both the UI and through an application object exposed in the API.

The application object is accessible through the msls library object as “msls.application”, but also more directly through an alias to the same object in the global space called “myapp”. This object is strongly typed with all the entities and screens that have been defined for the application as well as the set of built-in methods that are called by the shell:

Application

This snapshot is taken from the example application we have been building. You can see the Order entity and OrderDetail screen assets in the dropdown, and the selected item is a generated show screen method for the OrderDetail screen. Notice that the parameters to this method include “Order”, which is the designed screen parameter for this screen. The completion list also shows some of the built-in methods like navigateHome() and saveChanges(). The onsavechanges event is not covered in this post, but in brief, it allows you to customize what it means to save when there are changes pending across multiple data services that cannot be transacted as a single unit.

Let’s make one last change to the example application: alter the ImportOrders screen method so that it imports orders sorted by order date and then automatically navigates to edit the order with the most recent order date. Here is the new code snippet (added/changed lines are highlighted in yellow):

myapp.BrowseOrders.ImportOrders_execute = function (screen) { 
  // Write code here.  
  var northwind =
"http://services.odata.org/Northwind/Northwind.svc" 
  return msls.promiseOperation(function (operation) { 
  OData.read({ requestUri: northwind +
"/Orders?$orderby=OrderDate&$top=10"
               recognizeDates:
true
               enableJsonpCallback:
true }, 
               function success(result) { 
                 var results = result.results, importedOrder;  
                 for (var i = 0, len = results.length; i < len; i++) { 
                   importedOrder = screen.Orders.addNew();  
                   importedOrder.OrderDate = results[i].OrderDate; 
                 } 
                 screen.ImportPerformed =
true
                 myapp.showOrderDetail(msls.BoundaryOption.nested, importedOrder); 
                 operation.complete(); 
               }, 
               function error(e) { operation.error(e); }); 
  });
};

The OData URI is altered to include an orderby operator, and a line is added to show the order detail screen for the last imported order. The first parameter to this method – a boundary option – is used so the caller can tell the target screen how it is being hosted. In this case, the nested boundary option is used which means that the target screen begins a nested change set over the data and shows OK and Cancel buttons in the UI. You can find more information about the available boundary options in my previous architectural post.

This is not a very interesting example, but it does illustrate the usage of a show screen method on the application object and how you can integrate screen navigations into the rest of your business logic.

Debugging Support

Finally, a short word on debugging. For the June Preview, we had suggested that you use the F12 debugger in Internet Explorer or another browser to debug code because the integrated experience in Visual Studio was not yet available. With Preview 2, you can now set breakpoints in your original source code and they will be hit if you run your project in Internet Explorer under F5:

Debugging

This support greatly simplifies the experience for debugging your custom LightSwitch code from inside Visual Studio.

Summary

The LightSwitch team has made a number of improvements to the coding experience for the HTML Client Preview 2, spanning design-time experiences, available coding entry points and additional runtime APIs. This is still a work in progress, and we are eager to drive our next wave of API work using your feedback, so please use the HTML Client forum to post any questions or comments you have on what you have seen here.

Thanks for reading!

Stephen Provine
Principal Software Design Engineer
Visual Studio LightSwitch Team

Leave a Comment
  • Please add 6 and 1 and type the answer here:
  • Post
  • When are Microsoft releasing the preview version of Microsoft Parallel Lives, so I will have enough time to play with all these amazing new features!

    Thanks to the whole Lightswitch team for the brilliant new features, and the direction in which Lightswitch is going. This tool has gone from brilliant to absolutely amazing!

  • Looks like I'm going to eventually be dragged into the world of "web applications" & "JavaScript", whether I like it or not, lol. All those years of avoiding "curly braces" & semi-colons" wasted!

    Resistance is futile, I get it. :-)

    It's good to see that you guys are hard at work trying to make it easier for us all to use.

  • One word: AWESOME!

  • This is amazing.

  • Really very interesting and promising direction for the HTML Client. Great work, I am sure there will be loads of feedback, a reflection upon the high level of interest. Many thanks!

  • This is all well and good, and kudos to the team for achieving this, but, when will we see this functionality for the browser, not just mobile.

  • Thats so cool! Play time!

  • cool day 2.5 with LS and moving back into the web world rather than the black box of lsml. now just got to upgrade to 2012...

    Some cough when they see the word javascript, me , well I see a world of endless possibilities :)

  • Hi Stephen,

    working through all the channel 9 lightswitch clips I've made it down to the 'Early Look at the Visual Studio LightSwitch HTML Client' clip, Im still trying to get my head around the binding model in the silverlight web client for 2011 and there are some things Im still trying to work out) the first thing that I noticed during the clip that didn't make sense to me (though now with the html client I feel more at home as my background is asp.net web rather then silverlight/ria) and some else who commented on the video clip also mentioned this - why not knockout?(doesn't sanderson work for ms now?)- why create your own binding? Im not trying to be critical (believe me, Im at a point in my career where I'm over that!) Im rather trying to understand why you guys went for a custom method rather than re-use i.e. like using jquery rather than writing your own library... maybe there is a good reason for this, maybe things might change as I make my way down the clips and dig into the html client code once I get a chance to crack open the sample html client app in vs2012... Its just that the Joe Binder in the clip mentioned something that does make sense re they struggled getting that silverlight client side binding goodness replicated in an html client, this is probably something that all web developers can relate to - did they speak to sanderson, I just feel like it seems that some reuse or learnings from other teams might have been missed here... I also realise that its easy to plug in libraries as well so I could just reference knockout, but it would have been an extra bonus having the designer do the mvvm boiler plate stuff for me, rather than as mentioned in the clip 'struggle' do some binding logic. (the sample in the clip wasnt really a struggle but I would think a complicated ui might become a bit more of a challenge)

    I'm seeing a convergence going to happen, lightswitch html will abd probably has already started to 'leak' into the web development community, I can see a massive business benefit from it, and naturally what was aimed at the systems or business person will become a development option rather than tool. This is evident by the hubs of non ms people in the community that are providing feedback and helping others - they mostly developers...

    hopefully you get alerts on your blog post as I'm really interested to get feedback on this even if you email me direct.

    good work to the team!

  • @deanvanrooyan: Thanks for your comments. We did spend some time evaluating Knockout, and while it has a bunch of really great features around binding and aids for control templates, we ultimately decided to go with our own system for a few reasons.

    Probably the biggest one was how we differ in thinking about MVVM. Knockout implements a traditional MVVM model, where the model layer consists of plain objects that are created and submitted through AJAX calls, and the view model layer is where all the magic lives. In LightSwitch, we blur the distinction somewhat by pushing a level of logic into the model layer (the various entities, data services and data workspace objects), which includes entity lifetime and relationship management, change tracking, a certain amount of validation and additional state information like whether data is read only or loaded that can be effectively utilized by a view model. Some of the powers of LightSwitch come from the fact that this information is in the data model, and can then be utilized by all view models that reference that data.

    This blurring of distinction means there is more tight, two-way interaction required between the view model and model objects. This might be somewhat possible using Knockout, but we didn't want to start pushing it in directions it wasn't primarily designed to go.

    A second reason had to do with API experience. Knockout observable objects have a particular look and feel to them that didn't fit in well with the rest of our APIs or translate over well from the LightSwitch APIs on the server or in Silverlight. While the HTML client APIs are obviously different in some respects, such as requiring async programming, we wanted to maintain a level of similarity so a seasoned LightSwitch developer wouldn't have to learn things all over again.

    There were some other reasons as well, but those were smaller and more technical, mostly relating to how well the binding system integrated with the rest of the tool, since, after all, LightSwitch has a lot to do with integration between components. Hope this answers your question!

  • @Stephen, as I read your blog it sounds like this HTML Client version is going to be an awesome product. I'm truly excited. In SL version we used C#/VB for both client and server, but with the HTML version, are we going to basically use JS/HTML5 on the client side and C#/VB on the server? Not that I have any problem with that, just confirming?

    Secondly, will we be able to use third party widgets to extend the UI look? Like Sencha or Kendo UI or Wijmo?

    You guys get this right, you'll have a killer product.

    ..Ben

  • Ben, that is correct. JS on the client, C# or VB on the server.

    As for third party widgets, we tried to avoid doing anything that would stop you from incorporating these into your application. Take a look back at one of our previous blog posts (blogs.msdn.com/.../writing-javascript-code-in-lightswitch-joe-binder.aspx). This is slightly out of date as it targets the first preview, but the concepts and capabilities are the same regarding the render and postRender methods.

    Please let me know if you have any other questions!

  • Thanks Stephen for the reply; Looks like Joe just popped up a new blog that will add more info.

    Very excited about this version coming up.

    ..Ben

  • I've experienced a couple of issues with the IntelliSense in the P2 Client:

    1) When I created a "home" screen and added some buttons, that seems to have thrown the IntelliSense file buider for a loop. I dug around and found that in my viewModel.intellisense.js file, several lines of code for my "home" screen were missing commas at the end of the line. That would cause ALL of the intellisense to be broken. I realised that I cuold go into that viewModel.intellisense.js autogenerated file and add the commas and save the file. intellisense would then work - until LightSwitch regenerate the file and break it again. Ugh.

    2) For some other reason that I cannot figure out, intellisense cannot seem to ever work on my entities, regardless of where I'm adding code. I looked through all of the files referenced for intelliSense ("viewModel.js", "data.js", "viewModel.intellisense.js") and none of them have any coding issues (like missing punctuation) so I have no idea where the issue is coming from. When inetellisense pops up, all it says is "IntelliSense was unable to determine an accurate completion list for this for this expression. The provided list contains all identifiers in the file."

  • Christopher, thanks for the feedback. I do recall there were some unfortunate issues with intellisense that were discovered shortly after the release of Preview 2, but I don't remember there being something in the view model intellisense file. It would be great if you could send me a snippet of the generated code that is incorrect and I'll take a look.

    As for intellisense not working at all, we have discovered that intellisense can sometimes be intermittent and has to do with the language service caching invalid information. Sometimes it helps to walk the reference chain (the files described by the <reference> tags) and keep trying to get intellisense at the bottom of each referenced file until it starts working. This has the side effect of invalidating these caches and usually gets it working again. If it never works for you, then there is probably something else going on. If you're able to describe a simple scenario that is broken for you, I can try to reproduce it and get back to you.

Page 1 of 2 (28 items) 12