Archive | EL RSS for this section

More on Using Dynamic Value Binding to Create a Reusable Field

In my last post, I showed how to use dynamic value binding to create a custom control to reuse a field. In this post, I’ll describe another use case, dig into the functionality a little further, and show one way to enhance it.

Another Use Case

Another use case for this functionality — the case where I first used this functionality — is a scheduling application.

I have a schedule form set up to display the schedule for a single day, but it has to work for any day of the given week.

Rather than create 7 sets of fields to bind them to the appropriate document fields or leave all fields unbound and write code to read values and write them the correct fields, this design pattern allows me to dynamically bind the fields and pass in an index for the specific day being displayed.

In addition, there’s jQuery code activating a type ahead feature, code that runs on a field event, validation, etc. All of this would be more tedious and complicated to manage if repeated over and over again dozens times on the same form.

Using Dynamic Field Binding

As I showed in the previous post, here’s an example of a combobox that’s on a reusable custom control and set up for dynamic value binding:

<xp:comboBox id="comboBox1"	
  value="#{compositeData.dataSource[compositeData.MyFieldName]}">
  <xp:selectItem itemLabel="Strongly Agree"></xp:selectItem>
  <xp:selectItem itemLabel="Mildly Agree"></xp:selectItem>
  <xp:selectItem itemLabel="Neutral"></xp:selectItem>
  <xp:selectItem itemLabel="Mildly Disagree"></xp:selectItem>
  <xp:selectItem itemLabel="Strongly Disagree"></xp:selectItem>
</xp:comboBox>

This code works as is to bind to the data source and field name passed into the custom control when it’s added to the XPage.

It is important to understand, though, that there is a limit to how dynamic the field binding can be; it cannot execute a computation within the value binding.

Let me explain.

In my scheduling application, I have a set of fields that is reused for any day of the week. Say that the field name on the underlying document is Date_1 for Sunday, Date_2 for Monday, and so on, and I’m storing the current day of the week in a sessionScope variable called DayNumber.

It does not work if the field binding is set up like this:

value="#{compositeData.dataSource[compositeData.MyFieldName + sessionScope.DayNumber]}"

However, that doesn’t mean it’s not possible — it can be done with the original version of the value binding shown in the first code snippet. The solution is to execute the computation before passing the field name into the custom control.

<xc:ccDynamicallyBoundField
  dataSource="#{javascript:return document1;}"
  Rating_FieldName="#{javascript:return 'Date_' + sessionScope.scheduleDay;}">
</xc:ccDynamicallyBoundField>

Accessing the Value of a Dynamically-Bound Field from the Data Source

As an enhancement, I wanted to have a computed text control that displays the value in read mode, because comboboxes don’t display nicely in read mode.

While trying to use this with two different data sources (one a document and the other a JSON object), I noticed that it’s a little more complicated to compute within SSJS because it’s not as powerful as EL in its resolution.

I can bind the data source with this format dataSource[FieldName], but, although that syntax works with a JSON object, SSJS cannot use that same syntax to retrieve a field from a document.

In order to work with both types of data sources, I had to enhance the computed text control to check the data source type and treat it differently if it’s a document.

This syntax does the job:

if (typeof compositeData.dataSource == 'NotesXspDocument') {
  // Data Source is Notes Document
  return compositeData.dataSource.getItemValueString(compositeData.MyFieldName);
} else {
  // Data Source is JSON
  return compositeData.dataSource[compositeData.MyFieldName];
}

Using Dynamic Value Binding to Create a Reusable Field

In XPages, you can compute the value binding of an input control. In this post, I’ll show how to use the powerful capability in order to streamline your design by creating custom control that allows you to reuse a field as needed for consistency, flexibility, and ease of maintenance.

A Simple Use Case

A simple use case for this functionality would be a survey application where forms could consist of many questions with similar sets of options for a response. Rather than creating the same combobox or radio button group over and over, you could have a single field designed as needed and reused for all questions that require the same options.

This would be especially useful if you develop dynamic form-building capability that allows users to specify any number of questions and build the form on the fly.

Advantages of this Design Pattern

There are several advantages to modularizing the design in this way, rather than creating several instances of a field with the same design over and over.

  • Easy to reuse
  • Easy to maintain list values, styling, etc
  • Easy to maintain more complex functionality (event handling code, validation/error handling)
  • Ability to bind the field to a document data source or some other data source

Creating the Reusable Field Custom Control

1) Create a custom control and add custom properties

Create a property for the field name / object property name. This will define where the data will be stored. This property is a string.

Create a property for the data source. This step isn’t required if you just want to assume the data source name (e.g. if all fields will be stored on document1), but it allows you to make the custom control much more flexible.

Set the property type to be a data source. Click the folder icon next to the Type field, expand Data Sources, and select dataInterface.

DynamicValueBinding_2_DataSourceProperty

You can set the Editor to be a Data Source Picker, but it doesn’t quite work the way you’d expect, so it’s not necessary. (Explanation below.)

DynamicValueBinding_1_CustomProperty

2) Add the reusable field and set its value binding. In this case, I have a combobox with 5 standard rating values that will be reused throughout the page.

<xp:comboBox id="comboBox1"	
  value="#{compositeData.dataSource[compositeData.Rating_FieldName]}">
  <xp:selectItem itemLabel="Strongly Agree"></xp:selectItem>
  <xp:selectItem itemLabel="Mildly Agree"></xp:selectItem>
  <xp:selectItem itemLabel="Neutral"></xp:selectItem>
  <xp:selectItem itemLabel="Mildly Disagree"></xp:selectItem>
  <xp:selectItem itemLabel="Strongly Disagree"></xp:selectItem>
</xp:comboBox>

The Power of EL

The computed value binding for the field in line 2 above is what makes this flexible design pattern possible. It combines the flexibility of XPages (allowing dynamic value bindings) with the power of Expression Language in order to define a value binding that is flexible enough to work with multiple types of data sources.

The EL syntax of dataSource[propertyName] will evaluate for an object property as well as a document field name!

compositeData is the object that provides the handle to all property values passed into a custom control, so compositeData.dataSource evaluates to the data source that is passed in. compositeData.Rating_FieldName is the property for the name of the field to which the value will be bound.

Using the Custom Control

Here’s an example of the source of an XPage that will use 3 instances of the field. In each case, it passes in the same data source but a different field name.

Question 1:
<xc:ccDynamicallyBoundField 
  dataSource="#{javascript:return document1;}"
  Rating_FieldName="Field1">
</xc:ccDynamicallyBoundField>
<xp:br></xp:br>

Question 2:
<xc:ccDynamicallyBoundField
  dataSource="#{javascript:return document1;}"
  Rating_FieldName="Field2">
</xc:ccDynamicallyBoundField>
<xp:br></xp:br>

Question 3:
<xc:ccDynamicallyBoundField 
  dataSource="#{javascript:return document1;}"
  Rating_FieldName="Field3">
</xc:ccDynamicallyBoundField>

Passing the Data Source

I mentioned earlier that you can set up the data source custom property of the custom control to be a data source picker (screen shot above shows it), but that it doesn’t work as expected.

It appears to work in that it lets you choose from a list of data sources on the page, but it does not actually work to pass the data source! It essentially passes a string with the name of the data source. The property looks like this:

dataSource="document1"

Instead, you need to compute the property that’s being passed to the custom control to return the data source:

return document1;

This sets the property passed to the custom control as shown in the code snippet that uses the custom control earlier in this post.

I’ve tested this with a document data source as well as a JSON object data source.

Up Next

In the next post, I’ll describe another use case and show how to extend this functionality a bit.

XPages Tip: EL Statements Do Not Work in JavaScript Library Functions

You may already be aware that you can use Expression Language (EL) to evaluate information on the server into client-side JavaScript code. However, this does not work in a library function. This post explains how modularize your code and still use EL to read server-side information into client-side JavaScript.

Here’s how it usually plays out:

  1. You realize that you need to access the value of an xp:inputText control in client-side JavaScript and use EL syntax in button code to retrieve that value. It works well.
  2. You decide that you’re going to be a good developer and modularize your code for reuse, so you move it to a script library.
  3. The code fails at the point where it tries to evaluate the EL statement.
  4. <facepalm>
  5. You update the library function to accept values evaluated with EL on the page to make it work properly.

An Example

Here’s a page with an xp:inputText (inputText1) and an xp:button to display the value.

alert('Value: ' + dojo.byId('#{id:inputText1}').value);

Blog_EL_CSJS_1

This works because #{id:inputText1} is evaluated on the server and it returns the actual client-side ID for that element and passes it into the client-side javascript code on the button.

When the page is rendered, it generates this function (and then uses dojo to attach it to the button):

function view__id1__id4_clientSide_onclick(thisEvent) {
  alert('Value: ' + dojo.byId('view:_id1:inputText1').value);
}

Notice that there’s a fully-fleshed out ID where I previously had an EL statement.

If I want to use that code more than once, I’d want to move it to a script library function:

function displayFieldValue() {
  alert('Value: ' + dojo.byId('#{id:inputText1}').value);
}

But when I try it, it doesn’t work. Nothing happens in the UI, but there’s an error that I can see in Firebug:

Blog_EL_CSJS_2

The error says that it couldn’t find the field so I can display the value. This is because EL statements do not evaluate in script library functions. They only evaluate in code that is on the page.

Modularization

There is a middle ground, though. If you have code that you want to re-use, you can still move it to a script library — you just have to evaluate EL statements on the page and pass them into the library function.

In this same example, change the library function as shown:

function displayFieldValue(fieldID) {
  alert('Value: ' + dojo.byId(fieldID).value);
}

Then call it from the button like this:

displayFieldValue('#{id:inputText1}');

And now it works!