Promises in LightSwitch (Justin Anderson)

Promises in LightSwitch (Justin Anderson)

Rate This
  • Comments 5

In previous posts, we’ve mentioned that the HTML client runtime uses promises to perform asynchronous execution. In this post, we’ll briefly explain what a promise is and show you where promises are provided in your LightSwitch application to allow you to asynchronously react to state changes in the application.

What Is A Promise?

A promise is an object that will return a fulfilled value at some point in the future. Promises allow you to chain together blocks of asynchronous code, where each will be synchronized on the result of the previous block of code. Normally, executing asynchronous code would be done by using event handlers, registering call backs, and maintaining some external state with the result of each operation. With promises, you can write code that looks like it is procedural but executes asynchronously.

In LightSwitch applications, the specific implementation of the promise pattern is the WinJS.Promise object.

At the end of this post, you’ll find links to the documentation on asynchronous programming and WinJS.Promise object details.  Although they refer to Windows and Windows Store apps, they are still equally valid in the context of a LightSwitch HTML application.

Example of Promises

Let’s take a look at a simple example of promises:

WinJS.Promise.timeout(3000).then(function () {
    this.alert("Hello, world!");
});

In the above example, there are actually two promise objects: one returned by the timeout method and the other returned by the then method. The first promise executes a timeout that waits 3 seconds before resolving its result. This promise does not return a value as a result, but will notify dependent promises when it is finished. The second promise waits on the first promise to be fulfilled and then shows an alert dialog stating “Hello, world!” Again, the promise does not return a value as a result. The combined effect is that, when this code is executed, the alert dialog will show after a 3 second delay.

Where Promises Are Used In LightSwitch

The following tables list where promises are used in a LightSwitch application.

Framework Methods

The msls object is the LightSwitch client library object that gives your application global access to LightSwitch client library methods and properties.

Method

Promise Fulfilled When

Fulfilled Result

Notes

msls.showMessageBox The message box is dismissed. msls.MessageBoxResult Its parameters are a message string as its first and an optional options object as its second.

It is recommended to only use this method in response to a user action, such as inside a custom button method, rather than *_created, *_render, or *_postRender methods.

Example using msls.showMessageBox method:

msls.showMessageBox("Yes or No", { buttons: msls.MessageBoxButtons.yesNo })
    .then(function (result) {
        switch (result) {
            case msls.MessageBoxResult.yes:
                this.alert("You chose Yes!");
                break;
            case msls.MessageBoxResult.no:
                this.alert("You chose No!");
                break;
        }
    });

The above shows an alert dialog with a message stating which message box button the user selected.

Application Methods

The myapp object is the application object that represents the instance of client application. It is strongly-typed and gives access to the active data workspace as well as the application methods, such as show screen methods.

Method

Promise Fulfilled When

Fulfilled Result

Notes

myapp.applyChanges Pending changes are finished merging into the parent change set. None  
myapp.cancelChanges Pending changes are canceled and app has navigated back to previous page. None  
myapp.commitChanges Pending changes are merged into parent change set (or if top level changes, pending changes are saved) and app has navigated back to previous page. None  
myapp.navigateBack App has navigated back to previous page. None  
myapp.navigateHome App has navigated forward to home page. None  
myapp.showScreen Target screen is shown. None Its parameters are the model identifier of the screen as its first, an optional parameters array as its second, and an optional options object as its third.
myapp.show<ScreenName> Target screen is shown. None This strongly-typed method is generated for each screen in the application. Its parameters are the screen properties that are marked as parameters in the screen designer in addition to an optional options object.

It is recommended to only use these methods in response to a user action, such as inside a custom button method, rather than a *_created, *_render, or *_postRender methods.

Example using myapp.commitChanges method:

myapp.commitChanges().done(function () {
    msls.showMessageBox("Changes saved successfully.");
}, function (error) {
    msls.showMessageBox(error.message, { title: error.title });
});

The above shows a message box with a success message if the changes are committed successfully, or a message box with an error message and title if the changes failed to be committed.

Entity Methods

Method

Promise Fulfilled When

Fulfilled Result

Notes

get<PropertyName> Query of the navigation property has finished executing. Type of the entity across the navigation property. This strongly-typed method is generated for each navigation property on the entity type.

Example using getManager method on an Employee entity that has a Manager navigation property:

screen.Employee.getManager().then(function (result) {
    msls.showMessageBox(result.Name);
});

The above shows the name of the manager of the current employee instance found in the Employee screen property.

Screen Methods

Method

Promise Fulfilled When

Fulfilled Result

Notes

closePopup Currently open popup is closed. None  
get<PropertyName> Query backing the screen property has finished executing. Type of the screen property (either single entity instance or array of entities). This strongly-typed method is generated for each screen collection property on the screen.
showPopup Target popup is shown. None Its parameter is the name of the popup to show.
showTab Target tab is shown. None Its parameters are the name of the tab to show as its first and an optional options object as its second.

It is recommended to only use these methods in response to a user action, such as inside a custom button method, rather than a *_created, *_render, or *_postRender methods. The exception is the get<PropertyName> method, which can be used at any time, as it does not explicitly involve UI interaction nor does it affect the navigation state of the application.

Example using get<PropertyName> method. In this example, there is an Employees collection property on the screen:

screen.getEmployees().then(function (employees) {
    employees.deleteSelected();
});

The above deletes the selected employee from a visual collection of employees.

Visual Collection Methods

Method

Promise Fulfilled When

Fulfilled Result

Notes

load First page of entities from query results has finished loading. The number of entities loaded. Can only be called if visual collection is not loaded.
loadMore Next page of entities from query results has finished loading. Object with items and startIndex properties. Can only be called if visual collection is loaded.
refresh First page of entities from query results has finished loading. The number of entities loaded. Can only be called if visual collection is loaded.

The above methods can be called from anywhere in a screen, as they do not explicitly involve UI interaction nor do they affect the navigation state of the application.

Example using the refresh and loadMore methods. In this example, there is an Employees collection property on the screen. This code could be used in a customer button method to allow the user to refresh the list of customers on a browse screen without having to forget which was the selected customer:

var selectedItem = screen.Customers.selectedItem

function loadMoreAndSetSelectedItem (collection, item) 
{
    if (collection.canLoadMore) {
        collection.loadMore().done(function (num) {
            setSelectedItemOrLoadMore(collection, item);
        });
    }
}

function setSelectedItemOrLoadMore(collection, item) {
    if (collection.data.indexOf(item) > -1) {
        collection.selectedItem = item;
    }
    else {
        loadMoreAndSetSelectedItem(collection, item);
    }
}

screen.Customers.refresh().done(function (num) {
    setSelectedItemOrLoadMore(screen.Customers, selectedItem);
});

The above code remembers the selected item of the Customers collection, refreshes the collection, and loads more of the collection until the same item is found. If the item could not be found again, no item is selected.

You can find additional examples of using promises and other screen code in LightSwitch HTML applications at How to: Modify an HTML Screen by Using Code.

Wrap Up

You’ll find that promises are used throughout the LightSwitch HTML Client API among the data and presentation layers of your application. With the use of promises, you can write readable code that looks like it runs synchronously, but actually executes asynchronously.

We hope that you find the information in this post useful and that it sheds more light on how promises are used in LightSwitch applications. If you have any problems trying to write promise code (or any problems in general), please drop by the MSDN LightSwitch forums and search for an answer or write a question.

- Justin Anderson, Software Development Engineer, LightSwitch Team

Resources

Leave a Comment
  • Please add 8 and 7 and type the answer here:
  • Post
  • For a moment I thought we would be promised something.

  • Really Great Article Justin!  Keep them coming.  

    Suggestion for next post:  How to use Client Query on HTML Client to filter data and load the results into a visual collection on the screen.  Like this unanswered forum post:

    social.msdn.microsoft.com/.../html-client-javascript-apply-filter-to-screen-data-collection

  • Great!

  • Awesome!!! Extremely helpful article which has made sense of lots of things to me.

  • Hi Justin,

    Would you please help me.  Haven't been able to get an answer on this from anyone.  Below is code to hide/collapse Menu Item/Ribbon Item.  Used code from Forum.  It works perfect as long as I use the "Standard Shell".  It doesn't work for the "Cosmopolitan Shell".  I checked the Cosmopolitan Shell Sample Code but couldn't see what I might need.  Thank you.

    Namespace LightSwitchApplication

       Public Class Application

           Private Sub SignIn_CanRun(ByRef result As Boolean)

               HideMenu()

               CollapseRibbonBar()

               HideRibbonBar()

           End Sub

           ' Hide the main menu altogether

           Public Shared Sub HideMenu()

               Dispatchers.Main.Invoke(Sub()

                                           Try

                                               MainMenu.Visibility = Visibility.Collapsed

                                           Catch

                                           End Try

                                       End Sub)

           End Sub

            ' Get the Main Menu Control

           Private Shared ReadOnly Property MainMenu() As NavigationView

               Get

                   Dim root = System.Windows.Application.Current.RootVisual

                   Return DirectCast(FindControlByName(root, "NavigationView"), NavigationView)

               End Get

           End Property

           ' Get the Main RibbonBar Control

           Private Shared ReadOnly Property RibbonBar() As RibbonCommandBar

               Get

                   Dim root = System.Windows.Application.Current.RootVisual

                   Return DirectCast(FindControlByName(root, "HomeTabItem"), RibbonCommandBar)

               End Get

           End Property

           ' Find a control with the specified Name (recursively) - returns null if not found.

           Public Shared Function FindControlByName(control As DependencyObject, name As String) As DependencyObject

               If control Is Nothing Then

                   Return Nothing

               End If

               If control.GetValue(FrameworkElement.NameProperty) IsNot Nothing AndAlso control.GetValue(FrameworkElement.NameProperty).ToString() = name Then

                   Return control

               End If

               ' When it is a ScrollViewer we need to use the .Content property

               If control.[GetType]().Equals(GetType(ScrollViewer)) Then

                   control = TryCast(DirectCast(control, ScrollViewer).Content, FrameworkElement)

               End If

               For i As Integer = 0 To VisualTreeHelper.GetChildrenCount(control) - 1

                   Dim child = FindControlByName(VisualTreeHelper.GetChild(control, i), name)

                   If child IsNot Nothing Then

                       Return child

                   End If

               Next

               Return Nothing

           End Function

       End Class

    End Namespace

Page 1 of 1 (5 items)