Writing JavaScript Code in LightSwitch (Joe Binder)

Writing JavaScript Code in LightSwitch (Joe Binder)

Rate This
  • Comments 10

UPDATE December 6th, 2012: With the release of HTML Client Preview 2 some of the code in this article may not work correctly. For updated information please see: Custom Controls and Data Binding in the LightSwitch HTML Client

----

With the HTML Client Preview finally available, we thought it’d be useful to drill into the JavaScript coding experience that’s included in the preview so you have some new things to try. If you’re interested in authoring custom HTML, CSS, or performing complex data binding, keep reading! If you’re just getting started with the HTML client, you may want to read my earlier post on creating screens before working through this one.

In keeping with the LightSwitch methodology, code in the HTML client is the primary mechanism by which you can express the logic and behavior that’s very specific to your application scenario. Code fills the gaps between the boilerplate client-server plumbing and the specific business logic requirements that can’t be generalized; it’s your escape hatch when you need to enable a capability LightSwitch doesn’t natively support. And while we always strive to improve the UI-based experience for designing the application, writing business logic in code is a core part of LightSwitch’s offering.

LightSwitch today supports the following types of code:

  1.  “Shared” entity business logic: defaulting field values, validation logic, computed properties. This code is run on the middle-tier and Silverlight client.
  2. Server-side business logic: behaviors associated with the Create, Read, Update, and Delete (CRUD) operations. This code is executed on the server when data is queried or received from the client during a save operation.
  3. Authorization logic: code that asserts the permissions required to access a screen, entity, or query.
  4. Custom query logic: complex LINQ expressions can be authored to intercept and augment queries defined in the designer.
  5. Screen and Presentation logic: behaviors associated with a button click; code that runs when a screen is created or before/after data on a screen is saved.

The HTML Client adds a classification of code entry points to customize or create UI programmatically. The Silverlight supports this type of logic through custom controls, but custom controls must be authored in a separate project, which can make some seemingly simple UI tweaks challenging. Based on feedback we’ve heard consistently in the forums, we wanted to make that process more natural for the HTML client. There are two main use cases we had in mind that pertain to customizing the UI programmatically:

  • Augment, restyle, or otherwise tweak the UI elements that LightSwitch creates. For example, if you’re displaying a list of invoices you may want to color the invoices that are overdue red. Or maybe you want to use custom CSS to restyle a built-in control on a specific screen.
  • Create custom UI on a specific screen. For example, use a jQueryMobile plugin for editing dates on a LightSwitch screen.

We used an event-based approach to enable each of the above use cases: The _postRender event allows you to add classes, restyle, and attribute the DOM elements LightSwitch creates; The _render event allows you to generate UI anywhere on a screen dynamically.

The _postRender event

The _postRender event is available on any control. It is fired after the underlying DOM element is created, allowing you to add classes, attributes, etc to the element before jQueryMobile expands it.

Consider a simple example to demonstrate the concept--let’s say I have a list of products and each product has a backorderd field. If the product is backordered, I want to display it in RED.

Product

Create a new “Browse” screen using the New Screen dialog that will display a list of products.

NewScreenDialog

Now create a ViewProductDetail screen, which we’ll link to from the BrowseProducts screen.

NewScreenDialog_ViewDetail

Now jump back to the BrowseProducts screen, and select the Product list in the designer. Configure the tap action for the list to show the View Detail screen.

EditTapAction

 

EditTapAction_Step2

Now select the item that represents the product list item and open the “Write Code” dropdown to select _postRender.

WriteCode_postRender

Once in code, you will notice that there are two parameters passed available in the _postRender function:

lightSwitchApplication.BrowseProducts.prototype.RowTemplate_postRender = 
function (element, contentItem) { };

The element parameter represents the root DOM/HTML element for the list item. In this case, that element is an <a> element because the list item is a hyperlink that launches the ViewProductDetail screen. The contentItem is the view model for the product—its value and meta-state, such as whether or not the product item is loaded, valid, etc.

To illustrate the basic functionality of each parameter, add the following code:

lightSwitchApplication.BrowseProducts.prototype.RowTemplate_postRender = 
function (element, contentItem) { if (contentItem.value.backordered) $(element).find("p").css("color", "red"); };

contentItem.value will return the product instance; we then check its backordered status and color the paragraph text for the list item accordingly using jQuery. If we run this code with some sample products, you can see that any backordered products are colored red:

clip_image011

This is a remarkably simple example, but it should give you a sense for the types of things you can do with the _postRender event. You can see another example—restyling the built-in list to use tiles—in an earlier post.

The _render event

While the _render event is useful for restyling existing UI, it does not allow you to introduce new elements into the DOM directly; the _render event exists to do just that. If you need to use a third-party control or want to manually insert custom HTML into the screen, use the _render event.

Let’s continue from the product example by inserting some custom HTML into our ViewProductDetail screen. Open the ViewProductScreen in the designer and add a “Custom Control” to the screen:

AddCustomControl

Use the Screen.Product.Name as the binding path for the control.

Binding UI

With the new custom control selected, open the Write Code dropdown and select the _render event. (Note: the _render event is only available for Custom Controls.)

WriteCode_render

The code for the _render event is pretty similar to _postRender: the element parameter is an empty <div> element, into which we can insert our custom HTML; and the contentItem is the view model that’s mapped to the binding path we provided above—Screen.Product.Name.

lightSwitchApplication.ViewProductDetail.prototype.Product_Name_render = 
function (element, contentItem) { // Write code here. };

We can use some simple jQuery to insert an <input> element that displays the product name.

lightSwitchApplication.ViewProductDetail.prototype.Product_Name_render = 
function (element, contentItem) { var customInput = $("<input type='text' value='" + contentItem.value + "' />"); $(customInput).appendTo($(element)); };

We can then run the app and see our new input element displayed.

clip_image016

Since the contentItem also contains all of the metadata for the name field, we can also retrieve things like the maxLength, validation results, and so on for the name.

lightSwitchApplication.ViewProductDetail.prototype.Product_Name_render = 
function (element, contentItem) { var customInput = $("<input type='text' value='" + contentItem.value + "' />"); $(customInput).appendTo($(element)); };

To illustrate what’s actually created after this code is executed, we can open F12 to inspect the DOM.

F12 Running

Data binding

Although the above demonstrates the _render event, setting the <input> value statically isn’t recommended: the value may not be loaded when the custom code is called, and we generally want the UI to update automatically when the underlying view model changes. LightSwitch provides a simple databinding API that is often used in conjunction with the _render function.

As seen in IntelliSense, the binding API accepts three parameters:

  • The source for the binding. If you’re authoring databinding in _render code, the source is typically the contentItem.
  • The relative path for the binding. In our example the path would be “value” since we want to bind to the contentItem’s value.
  • The update callback function, which is called when the bound property changes. The callback includes a parameter that represents the new value for the property.

binding IntelliSense

We can update our original _render code to use data binding as follows:

lightSwitchApplication.ViewProductDetail.prototype.Product_Name_render = 
function (element, contentItem) { var customInput = $("<input id='productname' type='text' maxlength='" + contentItem.maxLength + "'/>"); $(customInput).appendTo($(element)); msls.bind(contentItem, "value", function (newValue) { $('#productname').val(newValue); }); };

Now our custom <input> element’s value will be updated whenever the underlying view model changes. If we want to enable two-way data binding, we can use standard jQuery to listen for change events on our <input> element and update the contentItem accordingly.

lightSwitchApplication.ViewProductDetail.prototype.Product_Name_render = 
function (element, contentItem) { // Create the input element and add it to the DOM. var customInput = $("<input id='productname' type='text' maxlength='" + contentItem.maxLength + "'/>"); $(customInput).appendTo($(element)); // Listen for changes on the input element customInput.change(function () { // Make sure the value has really changed, then // update the view model accordingly if (contentItem.value != customInput.val()) contentItem.value = customInput.val(); }); // Listen for changes in the view model and update the input element accordingly msls.bind(contentItem, "value", function (newValue) { $('#productname').val(newValue); }); };

With two-way databinding configured, changes made in our custom UI will be persisted back the view model; and changes to the view model will be pushed to our <input> element.

There has been some great discussion on the forums surrounding databinding and other frameworks that offer richer data binding and templating, such as knockout and backbone. While we aren’t doing anything special in the HTML Client preview to integrate with them, doing so is something we continue to consider. Stay tuned!

Referencing and using third-party libraries

While our simple <input> example illustrates the _render functionality, most custom UI will use one or more JavaScript controls from the community. If you’re new to HTML development, some example libraries to check out include jQuery UI, wijmo, Kendo UI, NetAdvantage for JQuery, among many others. Regardless of the library you choose, there are a few tips and tricks that might make your experience for using them in LightSwitch a bit smoother.

Add script references above usercode.js

HTML client projects contain a default.htm file. Any script references should be added to this file. Make sure that you add the references before usercode.js to ensure your usage of the library works as expected.

<script type="text/javascript" src="Scripts/winjs-1.0.RC.js"></script>
<script type="text/javascript" src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.6.1.js" charset="utf-8"></script>
<script type="text/javascript" src="http://ajax.aspnetcdn.com/ajax/jquery.mobile/1.0/jquery.mobile-1.0.js" charset="utf-8"></script>
<script type="text/javascript" src="Scripts/datajs-1.0.0.js" charset="utf-8"></script>
<script type="text/javascript" src="Content/Resources/msls.prerequisite.resources.js"></script>
<script type="text/javascript" src="Content/Resources/msls.default.prerequisite.resources.js"></script>
<script type="text/javascript" src="Scripts/msls-1.0.0.js"></script>
<!-- Add your script references here -->
<script type="text/javascript" src="Scripts/jquery-ui-1.8.20.min.js"></script>

<script type="text/javascript" src="Scripts/Generated/data.js"></script>
<script type="text/javascript" src="Scripts/Generated/viewModel.js"></script>
<script type="text/javascript" src="Scripts/Generated/usercode.js"></script>

Call control constructors after the element is in the DOM

Most custom control frameworks operate by calling a constructor on a standard HTML element, such as <div> or <input>. The controls I’ve experimented with expect to be instantiated on a live DOM element; otherwise their layout logic may not work properly. For example, using a jQuery UI slider for the UnitsInStock field should look like this:

lightSwitchApplication.ViewProductDetail.prototype.Product_UnitsInStock_render = 
function (element, contentItem) { var myslider = $("<div id='myslider'></div>"); myslider.appendTo($(element)); // After the <div> has been added to the DOM, we can create the slider $('#myslider').slider(); };

Use jQueryMobile 1.0 or jQueryMobile 1.0.1

Our runtime currently does not work with jQueryMobile 1.1, so check to make sure the third-party libraries you’re using don’t depend on jQueryMobile 1.1. (We haven’t formally tested jQuery 1.7+, but generally using newer versions of jQuery is not an issue.)

If you encounter any other issues using third-party libraries in LightSwitch, please let us know on the forums and we can help sort out the issue(s).

Debugging

Early in the planning process for the HTML Client, the team agreed that—given the emergent nature of standards-based, mobile web development—it was important to make a preview build of the HTML client available as soon as possible: we wanted to start gathering feedback while we had sufficient time to respond to it. Releasing an early preview meant that a number of features and experiences are incomplete or altogether absent. The interim feature gaps are particularly noticeable in the coding and debugging space—basic things like IntelliSense, debugging, and APIs aren’t quite there yet.

If you’re trying out the preview and start writing code, you’ll want to use IE to debug your code--breakpoints set inside Visual Studio will never be hit if you F5 the application. If you would like to debug code, use Ctrl+F5 to launch IE then press F12. This is the process I typically use to debug my code:

1. Ctrl+F5 inside Visual Studio to launch the application

2. Press F12 to launch the debug window in IE

3. Switch to the script tab

clip_image021

4. Press “Start debugging”

5. All of your user code is copied into “usercode.js”; select that file in the dropdown

clip_image022

6. Set your breakpoint(s); reload the page if necessary

Breakpoint hit

We’re working to enable Visual Studio debugging presently, but this somewhat cumbersome process will allow you to debug your code in the meantime.

Up next

The APIs included in the HTML client preview are pretty minimalistic and are designed specifically for customizing UI. There are quite a few gaps to fill in the API surface area in our next iteration—most notably:

  • Screen events and APIs: Entry points that allow you to introduce logic when screens are loaded, data is saved, and commands are executed (e.g., button tap)
  • Client-side data logic: Defaulting entity field values, responding to change events, and so on

In the meantime, please let us know if you have any questions, comments, or run into issues with the code entry points in the HTML client preview. The HTML Client Forums are a great place to get a speedy response.

In the next post, we’ll cover the ins and outs of CSS and theming the application. We always appreciate hearing your feedback, so please keep it coming!

Joe Binder

Senior Program Manager, Visual Studio LightSwitch

Leave a Comment
  • Please add 8 and 5 and type the answer here:
  • Post
  • Thanks for the Javascript und HTML hell in Light switch.

  • Will we have a postRender equivalent for silverlight client? Conditional formatting is one of the most demanded features at the "submit idea" forum.

  • @Jens, please understand that we're referring to *customizing* the application with JavaScript.  The app composition, client-server interactions, shell navigation, theming, and screen layout are still code-less experiences.  LightSwitch has always provided direct access to the underlying technologies it uses.  While lower level technology interactions often entail another level of complexity, they also unblock certain scenarios that would otherwise be out of reach with LightSwitch.  The web landscape continues to move towards richer client-side interactions and JavaScript is the lingua franca for expressing them.  We didn't feel it would be useful to offer a modern web development tool that didn't include JavaScript.  Also, recognize that many of the quirks in the tooling experience I mention are short-term issues that are specific to the client preview--I only mentioned them in case folks ran into issues and were interested in a workaround.

    @Marcelo: Indeed, the feedback expressed on the forums with respect to conditional formatting motivating the postRender event.  It is on our backlog for the Silverlight client, though we have not yet sorted out the release timing for it.

  • with all new fetures in LS the sky is the limit......

  • Nice, Thanks for this.

  • Joe you did it again. This article is a MUST READ. It actually makes me WANT to write JavaScript! :) :)

  • Thank You Joe

    This is a very useful article.

  • Can we run such apps (Lightswitch) on Android 4.x?

  • @Saurabh Verma - Yes you will be able to run LightSwitch HTML apps on Android 4.x

  • It seems like preview2 is outputting only input type="text" for number fields.  Will there be support for html5 input type="number" and input patterns in later releases?

Page 1 of 1 (10 items)