After releasing the preview of our HTML Client to a broader audience, it became pretty clear from your feedback that we had to work on the user experience of LightSwitch HTML applications. While not everybody is an expert in user experience design, it is the core of any modern application and a driving factor for user adoption.
Meanwhile the world didn't stand still. Windows 8 was released with an amazing repository of guidance, the release of SharePoint 2013 brought a similar set of UX design guidelines targeted at modern apps and our base platform, jQueryMobile arrived at version 1.2, which added some great features that perfectly aligned with that overall picture. (Just a few days ago they actually released version 1.3 and we are working hard on keeping up again.)
In an effort to keep pace with those developments we had to do our homework first. We did a lot of research around existing user experiences. Based on that we had to rethink some of our underlying primitives, while making sure to not come up with a completely new model. At the end of the day we hope that we succeeded by introducing a small amount of changes, leading to huge improvements in the overall usability of LightSwitch apps, while also paying attention to a lot of details that might go unnoticed.
The three areas we focused on were (1) the correct set of primitives, (2) navigation between screens and (3) the overall look and feel. The change that catches your attention first is probably our new light-theme, which nicely aligns with the default colors of SharePoint.
The new set of primitives
The screen designer highlights most of the new concepts after adding your first screen. Screens now have a different set of properties, Show As Dialog and Screen Types, Dialogs have been renamed into Popups and there is a Command Bar as part of every tab. I'll try to explain how those changes all work hand in hand to create that new user experience.
The Screen remains the independent unit of work on the client. From a user perspective each screen has an isolated state and there is basically no shared data between screens. When navigating between screens we will keep track of the navigation stack and the current screen state so that it can be restored when you return. We introduced a new property that you can find in the properties view when selecting the screen node in the screen designer, Screen Type. This property can be set to either Edit or Browseand our screen templates default to appropriate values.Depending on this setting the shell will either render save and cancel buttons or just a back button. This new property basically replaces the Show Save Button property, but also does much more under the hood which I will explain later.
Dialogs already existed in different forms in our previous versions. One of the top feature requests we got though, was to make dialogs reusable or callable from multiple screens. Long story kept short: we did it. Dialogs are no longer part of individual screens. Instead a screen can be rendered as a dialog now. By setting the Show As Dialog property on a screen and navigating to that screen, it will be shown as a dialog at runtime. Dialogs darken the background and direct the user attention to them which makes them best suited for performing specific tasks, like edit operations, outside of the apps main flow. We set this property by default in the "Add/Edit Details" screen template. Opening up another screen from a dialog will make that screen render as a dialog as well, adding the ability to perform subtasks as part of one transaction.
Popups replace the dialogs node in the screen designer. Popups are a really lightweight way of showing additional information, much like Flyouts in Windows 8. In contrast to dialogs they do not show a dark background, show up locally to the users' tap action, do not render save or cancel buttons and close without confirmation when tapping outside. Good examples for using popups include displaying additional information about a selection, creating a filter, showing large images or asking the user for confirmation. Although you can use popups to edit data you have to take care of saving or discarding your changes manually using a combination of our own APIs and jQueryMobile's popup APIs. Internally we also use popups for our own controls, one example are our pickers. If you still miss the ability to do server side filtering in our pickers, try to use popups instead, and don't forget to blog about it ;)
Commands or buttons already existed in our last preview. We lacked a common place to put them though. Consequently we added a Command Bar to the screen designer. Every tab has its own command bar which makes it easy to display context related commands. Commands usually interact with screen data and we optimized our APIs to make those operations even easier. You can use the Icon property to select from one of the about 30 icons we provide out of the box. In addition to that you can add your own custom icons using CSS, just as you would do when using plain jQueryMobile. The only difference is the icon size, we use 36x36px for our icons instead of 18x18px.
Navigation between screens
Navigation between screens can be approached from two different perspectives. From a developer's perspective setting up the flow between screens and controlling the state of data should be as easy as possible. From a user's perspective the flow through the application and interactions should feel natural. We made changes to improve both perspectives.
It is easier than ever before to wire up screens. Previously, you had to first create all screens in order to then wire them together. Now, you just create a new command on an existing screen and we will provide you with a guided experience. I can tell you this is an absolute presentation winner. We will detect whether an appropriate screen for the selected command already exists and, if not, configure the screen templates dialog by filtering and pre-selecting a good set of defaults. We also removed the Task setting form the action dialog, so no puzzling about what Save, Ok/Cancel or None means anymore. We use the new Screen Type property to automatically detect that for you.
The save and cancel buttons always navigates back now instead of staying on the same screen. This will eliminate most of the appearances of the dialog asking whether you want to save or discard the current changes or stay on the screen. Where we had subtle differences between the behavior of dialogs and screens previously, we eliminated those and made it a purely visual choice. Nested changesets will automatically be created when navigating between two screens of type Edit and the APIs handle that in a completely transparent way for your custom code.
An example using Northwind
Enough theory, let's take a look at an example. I will connect to the familiar Northwind database using oData for this example and create an application to view and edit customers and also create orders. The screenshots illustrates the flow through the application. The first screen (Browse) shows a list of all customers with some details and allows the user to filter for specific customers using a popup. Adding a new customer is done using a dialog (Edit) which automatically adds the save and cancel buttons for you. Adding a customer doesn't necessarily require adding an order, so clicking on the save button navigates back to the previous screen. Using our API you can also navigate forward to the details screen instead, we will cover that in a later blog post. Although the details screen type is set to Browse, it allows for deleting the current customer using a command that interacts with our APIs. The “Edit” command will open up the same dialog as the “Add” command, showing the reusability. Using data binding you could also dynamically change the screen title from "Add Edit Customer" to something more meaningful, we already covered that in the “Contoso Moving” HTML Client tutorial. The “Orders” tab shows the list of all orders, allowing you to add new orders.
Using nested scopes for transactions
Certain objects only make sense if they exist together. Orders and order details is an example for that. An order detail cannot exist without an order, this is a parent-child relation. Creating orders has to be encapsulated into a transaction therefore. Clicking "Add Order" will show a dialog of type Edit with multiple tabs. The second tab allows you to create individual order lines. Adding an order line opens up the second dialog which automatically creates a nested scope. There is no longer a save button on that dialog, but an ok button instead. Clicking ok commits the changes to the current transaction, but not to the database yet. Changes will only be committed to the database when clicking the actual save button.
The simplified API explained
If that sounds good to you, but you still wonder if that model is flexible enough for custom code, let's take a look on how to implement a delete command. There are only three API methods that you have to remember:
- myapp.applyChanges(): Calling apply will save all the changes of the current changeset. If only one changeset exists, it will save them to the database. If the current changeset is a nested scope, it will commit the changes to the parent changeset.
- myapp.commitChanges(): Calling commit will save all the changes of the current changeset, just like applyChanges. Afterwards the application will navigate back to the previous screen.
- myapp.cancelChanges(): Cancel will undo all changes in the current changeset and navigate back to the previous screen. Again, you do not need to worry about nested changesets, we will do that for you.
Applying that knowledge, the following code snippet shows you how to implement a delete command, including appropriate error handling using promise objects:
myapp.ViewCustomer.Delete_execute = function (screen) {
screen.Customer.deleteEntity();
return myapp.commitChanges().then(null, function fail(e) {
myapp.cancelChanges();
throw e;
});
};
A word on upgrade
If you already used the preview of the HTML client you might wonder whether you have to throw away your old projects and start over again. The good news is that those projects continue to load, and with a small set of changes they will also function properly again. After loading your project there are only three steps you have to do. First, you have to go through all of your screens and set the new screen type property appropriately. We default to Browse, but you need to change it to Edit for some of your screens. Second, all of your dialogs have been converted into popups. Since those do not include save or back buttons you might want to create new screens instead, using the new property show as dialog. Don't forget to wire up your navigation to show those new screens instead of the popups. Third, if you used our old APIs, make sure that you change all usages of saveChanges, discardChanges, acceptNestedChanges and cancelNestedChanges to the new commitChanges, applyChanges and cancelChanges. That's it, F5 and you are good to go again.
Wrapping it up
I hope that gives you a good overview about the changes we made and also why we made them. I know that I didn't cover all details on a step by step base. Feel free to use the forums and the comments section to stay in contact with us and stay tuned for coming blog posts which will go into some of the topics in more detail.
- Heinrich Wendel, Program Manager LightSwitch Team