One of my favorite things about the LightSwitch HTML Client is how easy it is to create forms over data the “LightSwitch way”, and then jump straight into HTML for a bit of custom tweaking. Because the HTML Client makes use of jQuery and jQueryMobile, these two framework are always at your disposal. Of course, you can leverage other frameworks as well, but for this post we’ll focus on building LightSwitch custom controls that “wrap” jQueryMobile functionality. The techniques and patterns you see here will transfer to other frameworks and control types as well.
To install the LightSwitch HTML Client Preview 2 head to the LightSwitch Developer Center.
If you’re just getting started with the LightSwitch HTML Client, you may want to peruse through some of the previous blog posts first.
- Announcing LightSwitch HTML Client Preview 2!
- Building a LightSwitch HTML Client: eBay Daily Deals (Andy Kung)
- New LightSwitch HTML Client APIs (Stephen Provine)
- Custom Controls and Data Binding in the LightSwitch HTML Client (Joe Binder)
OK, let’s get started!
Creating a table with two basic screens
For this post, we’ll start with a new “LightSwitch HTML Application” project, and create a very simple “Customers” table within it. This table will keep track of a customer’s name, family size, and whether or not his/her subscription is active.
To create the table, right-click on the “Server” node in the Solution Explorer, and select “Add Table”. Fill out the information as follows:
Next, let’s create a Browse Data Screen, using “Customers” for the data. Right-click on the Client node in the Solution Explorer, “Add Screen…”, and select “Customers” in the Screen Data dropdown:
Let’s create a second screen, as well, for adding or editing customer data. Again, right-click on the Client node in the Solution Explorer, and add another screen. This time, choose an “Add/Edit Details Screen” template:
Adding screen interaction
Now that we have two screens, let’s add some meaningful interaction between them. Choose the “BrowseCustomers” screen in the Solution Explorer to open the screen designer, and add a new button.
In the dialog that comes up, select “Choose an existing method”, and specify “Customers.addAndEditNew”. The “Navigate To” property is automatically pre-filled to the right default: the Customer Detail screen that we’d created.
If you drag the button above the list and F5, you will get something like this:
If you click the “Add Customer” dialog, you will see a screen that prompts you to fill out some information. It makes sense to have the Name be regular text input, but the Family Size and Active Subscription might be better suited for alternate visual representations.
At the end of this blog post, you’ll be able to transform the “baseline” screen above into something like this:
Setting up a Custom Control
Let’s start with transforming “Family Size” into a Slider control. jQueryMobile offers an excellent slider control, complete with a quick-entry textbox.
This control does not come built-in with LightSwitch, but we can add it as a custom control, with just a bit of render code.
First, navigate to your CustomerDetail screen. With “FamilySize” selected, open the Properties window, and change the control type from “Text Box” to “Custom Control”. You’ll also want to change the width to “Stretch to Container”, so that the slider can take up as much room as it needs.
Now that you’ve switched “FamilySize” to a custom control, navigate to the top of the Property window and select “Edit Render Code”:
A code editor will open, with a method stub automatically generated for you:
myapp.CustomerDetail.FamilySize_render = function (element, contentItem) {// Write code here.};
In the auto-generated function above, “element” is the DOM element (e.g.,
Let’s start with a simple question: assuming you did have a custom control that magically did all the work for you, what parameters would you need to pass to it? Obviously, “element” and “contentItem” from above are necessary. We’d also need to know the minimum and maximum values for the slider… and that’s about it.
A slider control seems like a reasonably generic component, so instead of writing all of the code in this one function, it might be worth separating the generic slider logic from the specifics of this particular instance. Thus, our function above can become:
myapp.CustomerDetail.FamilySize_render = function (element, contentItem) {// Write code here.createSlider(element, contentItem, 0, 15); };
Well, that was easy! But now, let’s actually implement the hypothetical “createSlider” function.
Rendering a Slider
The first thing our createSlider function needs to do is create the necessary DOM element. jQueryMobile’s Slider documentation tells us that the Slider operates over a typical tag. So a reasonably first attempt would be to do this:
function createSlider(element, contentItem, min, max) {// Generate the input element.$(element).append('+ min +'" max="' + max + '" value="' + contentItem.value + '" />'); }
On F5, you will see that the field indeed renders as a slider. Mission accomplished?
Unfortunately, not quite. If you drag another FamilySize element to the screen (just so you have a regular text box to compare to), you’ll see that the input slider does not seem to do anything. You can drag the slider up and down, yet the other textbox does not reflect your changes. Similarly, you can type a number into the other textbox and tab out, yet the slider is not impacted.
What you need to do is create a binding between contentItem and the slider control. To do this, you’ll actually create two separate one-way bindings: one from contentItem to the slider, and one from the slider to contentItem. Assume for a moment you already have a $slider object, bound to an already-created jQueryMobile slider. The code you would write would be as follows:
// If the content item changes (perhaps due to another control being // bound to the same content item, or if a change occurs programmatically), // update the visual representation of the slider:contentItem.dataBind('value', function (newValue) { $slider.val(newValue); });// Conversely, whenever the user adjusts the slider visually, // update the underlying content item:$slider.change(function () { contentItem.value = $slider.val(); });
How do you get the $slider object? The simplest way is to let jQueryMobile do its usual post-processing, but this post-processing happens after the render functions have already executed. The easiest approach is to wrap this latter bit of code in a setTimeout call, which will execute after jQueryMobile’s post-processing gets executed. See Joe’s Custom Controls post for another case – performing actions against a “live” DOM – where you may want to use setTimeout.
Putting it all together, the final createSlider code is:
function createSlider(element, contentItem, min, max) {// Generate the input element.$(element).append('+ min +'" max="' + max + '" value="' + contentItem.value + '" />');// Now, after jQueryMobile has had a chance to process the // new DOM addition, perform our own post-processing:setTimeout(function () {var $slider = $('input', $(element));// If the content item changes (perhaps due to another control being // bound to the same content item, or if a change occurs programmatically), // update the visual representation of the slider:contentItem.dataBind('value', function (newValue) { $slider.val(newValue); });// Conversely, whenever the user adjusts the slider visually, // update the underlying content item:$slider.change(function () { contentItem.value = $slider.val(); }); }, 0); }
When you F5, you’ll see that both Family Size controls – the textbox and the custom slider – stay in sync. Success!
Creating a Flip Switch for a Boolean field
Having created the Slider, let’s create another control for the Boolean field of “Active Subscription”. Just as with the Slider control, jQueryMobile offers a great Flip Toggle Switch (with customizable labels, if you’d like):
As with the “Family Size” field, we can go to the Active Subscription’s properties and switch it from a Text Box to a Custom Control. I will also change the width to “Stretch to Container”, change the Display Name to just “Subscription”, and finally click “Edit Render Code”:
myapp.CustomerDetail.ActiveSubscription_render = function (element, contentItem) {// Write code here.};
As before, let’s encapsulate the main functionality in a function, that we’ll call from the ActiveSubscription_render method. The information we want to pass in is similar to the Slider. We’ll need “element” and “contentItem”, and we’ll need the analog of a “min” and “max”: in this case, the labels for the true and false states. Reading over the Flip Toggle Switch documentation, we’ll also notice that width must be set explicitly in case for non-standard on/off labels. Thus, we’ll pass in a width parameter as well.
myapp.CustomerDetail.ActiveSubscription_render = function (element, contentItem) {// Write code here.createBooleanSwitch(element, contentItem, 'Active', 'Inactive', '15em'); };
Now for the fun part – the createBooleanSwitch function. Conceptually, the function is very similar to createSlider.
First we initialize the DOM:
var $selectElement = $('').appendTo($(element)); $('+ falseText + '').appendTo($selectElement); $('+ trueText + '').appendTo($selectElement);
Then, inside a setTimeout (which allows us to operate with the jQuery object for the flip switch), we:
1. Set the initial value of the flip switch (we did this in the DOM for the slider, but it’s a little trickier with the flip switch, so I’ve encapsulated it into a shared function that’s used both during initialization and on updating)
2. Bind contentItem’s changes to the flip switch control
3. Bind the flip switch’s changes back to the contentItem
4. One additional step, which we didn’t need to do for the slider: explicitly set the flip switch’s width, as per the documentation.
The resulting createBooleanSwitch code is as follows:
function createBooleanSwitch(element, contentItem, trueText, falseText, optionalWidth) {var $selectElement = $('').appendTo($(element)); $('+ falseText + '').appendTo($selectElement); $('+ trueText + '').appendTo($selectElement);// Now, after jQueryMobile has had a chance to process the // new DOM addition, perform our own post-processing:setTimeout(function () {var $flipSwitch = $('select', $(element));// Set the initial value (using helper function below):setFlipSwitchValue(contentItem.value);// If the content item changes (perhaps due to another control being // bound to the same content item, or if a change occurs programmatically), // update the visual representation of the control:contentItem.dataBind('value', setFlipSwitchValue);// Conversely, whenver the user adjusts the flip-switch visually, // update the underlying content item:$flipSwitch.change(function () { contentItem.value = ($flipSwitch.val() === 'true'); });// To set the width of the slider to something different than the default, // need to adjust the *generated* div that gets created right next to // the original select element. DOM Explorer (F12 tools) is a big help here.if (optionalWidth != null) { $('.ui-slider-switch', $(element)).css('width', optionalWidth); }//===============================================================// // Helper function to set the value of the flip-switch // (used both during initialization, and for data-binding) function setFlipSwitchValue(value) { $flipSwitch.val((value) ? 'true' : 'false');// Because the flip switch has no concept of a "null" value // (or anything other than true/false), ensure that the // contentItem's value is in sync with the visual representationcontentItem.value = ($flipSwitch.val() === 'true'); } }, 0); }
On F5, we see that our control works. Again, I put in a dummy textbox for “Active Subscription” to show that changes made in one field automatically get propagated to the other. You’ll notice that on initial load, the text field is set to “false”, rather than empty, as before. This is because of the flip switch has no concept of a “null” value, so I explicitly synchronize contentItem to the flip switch’s visual representation in the setFlipSwitchValue function above.
Removing the temporary “Active Subscription”, we get to this final result:
Next Steps:
Hopefully, this post whets you appetite for what you can do with Custom Controls in the LightSwitch HTML Client, and gives you a clear sense on how to wrap arbitrary controls into LightSwitch-usable components. Leave a comment if you have any questions, and, once again, thank you for trying out Preview 2!
- Michael Zlatkovsky, Program Manager