Creating a Dynamic Data-Driven User Interface (Part 2)By Scott Mitchell
This article is the second installment of a four-part series that examines how to build a data-driven web applications that offers dynamic user interfaces. Over the course of this article series we will build a complete and functional web application with a dynamic, data-driven user interface. Specifically, the demo application is a fictional website used by numerous law firms to manage their clientele.
The application uses both a fixed and dynamic data model for law firms to manage their clients. The
Clients table contains
the fixed attributes for a client and is composed of columns like
LastName. All law firms have these fixed attributes available to them. The dynamic data model allows each law firm to define
custom attributes for their clientele. For example, a law firm that specializes in personal injury might need to capture client information
that is not needed for a law firm that specializes in family law. The custom client attributes for each law firm are stored in a database table
Part 1 examined the scope of the project and created the data model. In this installment we create the web pages used by the law firms to define the custom client attributes. Read on to learn more!
When we left off in Part 1 our web application consisted solely of a database in the
App_Datafolder that implemented both our application's data model and the schema necessary for the SqlMembershipProvider. In the demo available for download at the end of this article you'll find that I have added many user interface aspects, including:
- A master page that implements a CSS-based website design created by John Zaitseff. For more information on this design, see http://www.opendesigns.org/preview/?template=1700.
- A site map file (
Web.sitemap) with links to the site's various sections.
- A login page, where visitors can log in to the website.
Customersfolder that has been configured so that only authenticated users can visit the ASP.NET pages within. (For more information on how to restrict certain web pages from anonymous users check out the User-Based Authorization tutorial in my Forms-Based Authentication, Membership, and Roles tutorial series.) In this folder you'll find the page
DefineClientAttributes.aspx, which is where a customer can create and manage custom client attributes.
DefineClientAttributes.aspxpage in detail. This web page enables the visitor to add new custom client attributes as well as edit and delete existing attributes.
Determining What Customer the Currently Logged On User is Associated With
When visting The
DefineClientAttributes.aspxpage we need to list the custom client attributes. Because these attributes are specific to a particular customer we need to determine what customer the currently logged on user is associated with. This information is stored in the
ExtendedUserInfotable, which we created in Part 1.
There are several scenarios where we'll need to determine the currently logged on user's associated
CustomerId value. For instance,
we need to determine the
CustomerId value in
DefineClientAttributes.aspx when listing the custom attributes and when
adding a new custom attribute to the database. Similarly, will need the
CustomerId value when generating the dynamic, data-driven
user interface, as we need to know what attributes to display in the UI. Therefore, let's create a helper method that returns
CustomerId value for the currently logged on user.
I have created such a method in a class named
Helpers, which is located in the
App_Code folder. This class
has a single shared function,
GetCustomerIdForLoggedOnUser, which can next to the database and queries the
ExtendedUserInfo table, returning the currently logged on users associated
CustomerId value. The code
for this class follows:
The currently logged on user's
UserId value is obtained by
Membership.GetUser() method returns information about the currently logged on user. The
property retrieves the
UserId value. This value is used for the value of the
@UserId parameter in the above
Viewing a Customer's Dynamic Client Attributes
DefineClientAttributes.aspxpage includes a GridView control that lists the dynamic client attributes that belong to the customer associated with the currently logged in user. This GridView is populated via a SqlDataSource control named
CustomAttributesDataSource. This data source control's
SelectCommandproperty is assigned to the following
Note that it returns all records from the
DynamicAttributesForClients table for a particular
It brings back the name of the data type for each attribute by joining on the
Finally, it sorts the results by the
SortOrder column in ascending order, breaking ties by sorting alphabetically on the
We need to specify a value for the
@CustomerId parameter. This can be done by creating an event handler for the SqlDataSource
Selecting event fires each time the data source control goes executes its
SelectCommand against the database.
The following event handler code assigns the value of the
@CustomerId parameter to the
CustomerId value associated
with the currently logged on user:
The following screenshot shows the GridView when viewed through browser. As you can see, the GridView lists the custom client attributes for the appropriate customer.
For more information on using the
Selecting event to programmatically specify values for the parameters in the
see the Examining the Data Source Control's Events article in my
Accessing and Updating Data in ASP.NET article series.
Adding a New Dynamic Client Attribute
In addition to viewing the dynamic client attributes, visitors need to be able to add new attributes. This can be accomplished in a number of ways. One of the easiest ways to insert data into a database from a web page is to use a DetailsView control in tandem with a data source control that is configured to support inserting. The
CustomAttributesDataSourcedata source control includes an
InsertCommandproperty that specifies the
INSERTstatement to use to add a new record to the
INSERT statement in place we can configure the DetailsView to support inserting. Moreover, the DetailsView can be
permanently rendered in insert mode by setting its
The values for the new attribute's
SortOrder need to be specified by the visitor.
CustomerId value, however, is based on the currently logged on user and can be populated programmatically via the
GetCustomerIdForLoggedOnUser method in the
Helpers class. To set the
CustomerId value create an event handler
for the DetailsView control's
event. This event fires at the start of the inserting workflow and provides an opportunity for developers to customize the values that will be used
when adding a new record. Simply set the
CustomerId value to the value returned by the
By default the DetailsView's inserting interface is composed of a TextBox control. While a vanilla TextBox is sufficient for some inputs, it is
less than ideal for those inputs that require some sort of validation logic or would be best captured using an alternative data entry Web control.
For example, the
DataTypeId field is a foreign key to the
DynamicAttributeDataTypes table. The inserting interface
should let the visitor choose the new attribute's data type from a drop-down list rather than requiring them to type in an ID value.
Likewise, the inserting interfaces for the
SortOrder inputs should include validation logic
since these are both required fields.
the following screenshot shows the DetailsView control in action. Validation controls are present to ensure that the user enters a name and sort order for each new attribute, and that this sort order is a numeric value between 1 and 100.
For more information on inserting data using ASP.NET's data source controls be sure to read Inserting Data. For more on how to customize the DetailsView control's inserting interface, see Adding Validation Controls to the Editing and Inserting Interfaces and Customizing the Data Modification Interface.
|Don't Forget to Remove the |
The SqlDataSource control's Configure Data Source wizard makes it easy to automatically generate |
To fix this problem simply remove the
Refer to Inserting with a SqlDataSource Using uniqueidentifier Parameters for more information on this bug and workaround.
Editing and Deleting Dynamic Client Attributes
Currently a single SqlDataSource control is used for selecting and inserting data into the
DynamicAttributesForClientstable. We can extend this control to also handle editing and deleting. Start by configuring the SqlDataSource control's
DeleteCommandproperties as follows:
Note that the
UPDATE statement does not allow for an attribute's data type to be modified. This restriction is in place
because once an attribute is in use and has values for clients changing the data type can cause the existing values to become corrupt.
Consider a custom attribute named "Reason for Law Suit" of type String. Imagine that a value for this attribute has been specified for
most of the clients for a particular law firm. Now imagine that the attribute type is changed to Boolean. When displaying information
for a particular client how would the system translate a value like "Suing for damages to automobile" - a perfectly valid value
when the attribute had a data type of String - a Boolean value?
Another potential issue can arise if the attribute's name can be modified, which is allowed in this demo application. Consider a Boolean attribute named "Active Client," and that dozens of clients have this value set to either True or False. Now imagine that a user changes the name of this attribute from "Active Client" to "Inactive Client."
One option would be to allow a user to modify the data type (or name) for any attribute that does not have associated values. I leave this as an exercise to the interested reader.
Once these command properties have been set you can configure the GridView control to support built-in editing and deleting.
Doing so adds a CommandField to the GridView control. Like with the DetailsView, we need to customize the GridView's editing interface to
include the necessary validation logic for the
SortOrder inputs and a drop-down list for the
That's all there is to it. Because the data shown in the grid is already filter based on the currently logged on user's
there is no need to include this field in the
DELETE statements. The following screenshot shows the
customized editing interface and action.
Assuming you configured the foreign key constraint between the
DynamicValuesForClients.AttributeId columns to cascade deletes when a visitor deletes a custom client attribute all associated
values for that custom attribute are automatically deleted. If you do not configure the foreign key constraint to cascade deletes then
a foreign key constraint violation will occur if a visitor attempts to delete a custom client attribute that has associated values.
This will result in a Yellow Screen of Death unless you
proactively prohibit visitors from deleting attributes that have associated values or if you explicitly delete the associated values
prior to deleting the attribute itself.
At this point we have created a data model and built the shell of a website to allow to log in and manage their custom client attributes. In the next installment we will turn our attention to the most complex task in building dynamic, data-driven user interfaces: programmatically constructing the user interface based on dynamic attributes defined by the customers.
Until then... Happy Programming!