Interacting with Content Items on the Screen with the LightSwitch API (Kevin Mehlhaff)

Interacting with Content Items on the Screen with the LightSwitch API (Kevin Mehlhaff)

Rate This
  • Comments 14

A LightSwitch application at its core is built by adding data to screens and creating a tree of content items to display and interact with this data. LightSwitch provides a JavaScript API over these content items so that you can interact with them programmatically.

What is a Content Item?

If you open a screen designer in LightSwitch, you will see the data members list on the left and the content item tree to the right:

screen05

Content items in the content item tree are bound to data in the data members list. To find out what each content item is bound to, you can select it, and view the Data Binding or Data Context property in the Properties sheet. Note that this cannot be set directly. You bind items by dragging them from the data members list onto the content item tree. Then the correct binding is automatically set:

screen06

How to Use a Content Item Programmatically

There are two main ways to get access to a content item programmatically. The first is off of the screen object. The method findContentItem takes in the Name of the content item. This can be used in the screen created entry point or in a custom method to find a specific content item to interact with.

For example:

myapp.AddEditCustomer.created = function (screen) { 
var contentItem = screen.findContentItem("Details");
};
myapp.AddEditCustomer.MyMethod_execute =
function (screen) {
var contentItem = screen.findContentItem("Details");
};

The second way is in the render method of a custom control content item or the postRender method of a content item rendered by LightSwitch. In these cases, the content item is passed directly to the function:

myapp.AddEditCustomer.Name_postRender = function (element, contentItem) { 
};
myapp.AddEditCustomer.Details_render =
function (element, contentItem) {
};

Content Item API

Once you have a reference to a content item in code, you can call several properties and methods on it. Here are some useful properties:

Name

Type

Settable?

Description

bindingPath

String

no

Gets the binding path that produces the “value” property

children

Array

no

Gets the children of the content item. These are child nodes of the item in the screen content tree.

data

object

no

Gets the source data object

description

String

yes

Gets or sets the description for this content item.

displayName

String

yes

Gets or sets the display name for this content item.

isEnabled

Boolean

yes

Gets a value indicating if the control for this content item should be enabled.

isReadOnly

Boolean

no

Gets a value indicating if the control for this content item should be read only.

isVisible

Boolean

yes

Gets or sets a value indicating if the control for this content item should be visible.

kind

String

no

Gets the kind (from msls.ContentItemKind) of this content item.

name

String

no

Gets the name of this content item.

screen

screen

no

Gets the screen that produced this content item.

stringValue

String

yes

Gets or sets the string representation of the “value” property.

validationResults

Array

yes

Gets or sets the current set of validation results for this content item.

value

object

yes

Gets or sets the value that this content item represents.

Here is the most useful method on a content item:

Name

Description

dataBind(String bindingPath, callback)

Binds to a source identified by a binding path.

The dataBind method is used to execute a callback function whenever the property at the end of the bindingPath changes. This is useful because it can keep the UI updated in response to changes to the data. For example, if you have a customer on a View Details screen and want to keep the title of the screen in sync with the name of the customer you can use:

myapp.ViewCustomer.Details_postRender = function (element, contentItem) { 
contentItem.dataBind(
"screen.Customer.Name", function (value) {
contentItem.screen.details.displayName = value;
});
};

If you are curious about the other properties and methods on a content item, Intellisense in the editor greatly helps in discoverability. Intellisense will also help you understand the type of object returned for the “data” and “value” properties.

The interaction between the data and value properties can best be understood with a couple of examples, illustrated in the table below:

Content Item

Data

Binding Path

Value

Text box displaying “name” property of Customer entity.

Customer entity

“data.Name”

String

A list of Customers

BrowseCustomers screen

“screen.Customers”

VisualCollection

In all, there are eight kinds of content items:

ContentItemType

Description

Example Controls

Value of ‘value’ property

Collection

Represents a query on the screen or a collection property.

List, tile list, table

VisualCollection

Command

A method either added as part of the command bar or anywhere on the screen.

Button

Screen object

Details

Represents either an item in a collection, a singular entity on the screen, or a navigation property.

Details picker, rows layout, columns layout, table row, summary property

Entity instance

Group

A group of content items.

Rows layout, columns layout

Screen object usually but the built-in content item groups of Tabs and Popups have value undefined

Popup

A popup on the screen.

Rows layout, columns layout

Screen object

Screen

The screen itself.

 

Undefined

Tab

A tab on the screen.

Rows layout, columns layout

Screen object

Value

A property of an entity that is not a relationship.

Text, date picker, money viewer, etc.

JS data type: Boolean, String, Number, Date

Example

Now that you understand what content items are and a little bit about how to use them, let’s try an example that will add additional functionality to an app. When a user of our app goes to add an entity, we want the user to be able to get additional information about a field so he or she best knows how to fill it out. We will do this by using the description property of the content item.

Create a new LightSwitch HTML Client app, and add an intrinsic Customer entity with the following fields:

screen1

Create a browse screen for the entity and add a new command bar button to the screen to addAndEditNew Customer.

On the newly created AddEditCustomer screen, select the two content items Name and Age and fill out their descriptions in the property sheet:

screen2

This is a pretty trivial description but you can imagine a use case where some explanation might be necessary. Next we will add code to show these descriptions to the user upon request. Add a popup called “Help” to display the description to the user. Add a Data Item to the screen, a local property of type String called “HelpText” that is not required:

image

Drag this new local property into the popup. Set its control type to be a paragraph and set its label position to none. Your screen content item tree should look like the following:

screen4

Next select the “columns” content item and edit the postRender method:

myapp.AddEditCustomer.columns_postRender = function (element, contentItem) { 
// Look for content items with type either 'details' (a navigation property)
// or 'value' (non-relationship properties)
var contentItemTypes = [];
contentItemTypes.push(msls.ContentItemKind.details);
contentItemTypes.push(msls.ContentItemKind.value);
// Find these content items starting from the children of the 'columns' content item
var matchingContentItems = findMatchingContentItems(contentItemTypes, contentItem);
// Find all LABEL elements that are descendants of the parent element rendering the
// 'columns' content item
var $matchingElements = $(element).find("label");
$.each($matchingElements,
function (index) {
// Set the LABEL element to float left
$(this).css("float", "left");
// Create a new A element that will display the '?' link
var $help = $("<a href='#'>?</a>");
$help.css({
"cursor": "pointer", "display": "block", "float": "right" });
var correspondingContentItem = matchingContentItems[index];
// Add a click event handler to display the content item description
$help.on('click', function (e) {
e.preventDefault();
contentItem.screen.HelpText = correspondingContentItem.description;
contentItem.screen.showPopup(
'Help');
});
// Insert the help element as a sibling after the LABEL element
$(this).after($help);
});
};

This postRender method selects all of the content items specific to editing the properties of a Customer. It then selects the HTML label elements that are displayed on the page, as this is what the user will see when editing the customer. Each label element sequentially corresponds to one of the selected content items. A question mark link is added next to each label with an on-click handler that displays a popup with the corresponding content item’s description.  The jQuery library is used for easy DOM manipulation.

Finally we add the helper method used by the postRender method above, findMatchingContentItems. This method takes in contentItemTypes, an array of ContentItemKinds, and parentContentItem, a content item from which to search. It recursively searches all of the children of parentContentItem and returns an array of content items that have a type equal to one of the specified ContentItemKinds.

function findMatchingContentItems(arrayOfTypes, parentContentItem) { 
var matches = [];
// Return an empty array if no children to look at
if (parentContentItem.children.length == 0) {
return matches;
}
$.each(parentContentItem.children,
function (i, contentItem) {
$.each(arrayOfTypes,
function (j, type) {
if (contentItem.kind == type) {
matches.push(contentItem);
}
});
// Check the child's children for matches
matches = matches.concat(findMatchingContentItems(arrayOfTypes, contentItem));
});
return matches;
};

Now F5 the app, and click the add button to display the AddEdit dialog:

screen5

As we can see each field now has a clickable question mark near it. Clicking the question mark opens the popup with the description of the content item:

screen6

Knowing how to interact with content items on the screen is key to creating a LightSwitch application that provides custom experiences for the end user. You can learn more about other aspects of the API with the LightSwitch HTML Client APIs tour or the use of promises in LightSwitch.

- Kevin Mehlhaff, Software Development Engineer in Test, LightSwitch Team

Leave a Comment
  • Please add 7 and 3 and type the answer here:
  • Post
  • Nice article!  Is it my browser, or is the code not wrapping so it's unreadable past the right margin?

  • The code should be readable now. Thanks!

  • Kevin,

    Really Brilliant !

    Many thanks

    paul van bladel.

  • Nice. The final example was really well done.

  • Very Nice !!!

  • It is nice piece of info, but there is no official msdn library reference for it???

  • Joe,

    You're correct re: lack of API reference- none, nada, zip , zilch.  Don't hold your breath either...

    social.msdn.microsoft.com/.../javascript-api-reference-for-html-client

  • The last few posts have been great.  Keep them coming!

    Vote: visualstudio.uservoice.com/.../5551121-include-fully-featured-grid-control-in-lightswitch

  • This approach does not work with custom controls. Function findMatchingContentItems should be generate accurate array of description. And again this is not white paper, just particular implementation. We need HTML client reliable description tooltiping as Desktop client does.

  • This is really good information Kevin! I hope the team will continue to write future articles like this, it's very helpful.

  • Nice article, but the contentItem does not have the ".description" property that you mention. Are you working with a different build than what we (the public) has? Thanks!

  • For Andrew Butenko: for Custom Controls, add this to the method: contentItemTypes.push("ScreenContent");

    For batpox: Most contentItems should have .description, unless they are controls like Tabs that do not have a property of description.

    Lightswitch is well designed, providing us with a rapid development environment with good extensibility, if one knows how that is. However,  it is a shame that most documents available are too basic to help with sophisticated commercial software development. I just hope that the reason for lake of in-depth documents is not because the user base is small.

  • @Rex (and others), we'd love to hear from you if there are specific topics you would like the team to cover. It looks like you have discovered a few ins and outs of the product and even just enumerating some of those things would help us plan topics for future blog posts.

    Thanks!

  • Stephen,

    I've shared many blog topic ideas - here, in forums and directly to team members via email.  July is nearly gone and we've not seen anything from the team blog nor anything LS related from team members personal blogs.  I guess the content rollup post will come next week but it'll be a short one.

Page 1 of 1 (14 items)