Creating Nesting Edit Table Panels in Iron Speed Designer
There are a few occasions where you'll want to implement nesting edit table panels to allow the mass editing of records and their child records.
- Gil Givati, CEO of Efficens Software Ltd.

October 24, 2007
Iron Speed Designer V5.0
Introduction
Parent-child relationships are very common in database driven web applications. Iron Speed Designer provides extensive support for such scenarios using Record Panels Wizards. The wizards recognize database relationships automatically. They also allow you to configure an application to query tables based on values contained in the record control. There are a few occasions where you'll want to implement nesting edit table panels to allow the mass editing of records and their child records. This article guides you through the steps to create nesting edit table panels.


A nested edit table panel in an Iron Speed Designer generated application.

The Data Model
I use the NorthWind database to display products purchased in each order, i.e. the OrderDetails. Our sample uses three tables: Employees, Orders (the order header) and OrderDetails. Our goal is to create an EditOrders page that allows us to modify an employee's orders.
Procedure
1. Create a new page and call it EditOrders. This page hosts an edit table panel that displays orders information. The orders are displayed according to the employee_ID that is passed as a URL parameter.

2. Add a data-bound literal to one of the cells in the edited table and call it Literal. This literal should be assigned to the value of the OrderID.

3. Add a new HTML table cell to the data row in the edit table and add another edit table panel in it. The new edit table displays records from the OrderDetails table with no filtering condition in this stage.

4. Add another HTML table cell next to the buttons of the newly created edit table. In this cell, position a label (not data-bound) called ReservationIdLabel and assign it the value of zero.

5. Add a data-bound literal to the data row and call it ReservationId. This literal should be assigned to the value of the OrderID field as well. Make sure this literal is marked to be saved to the database.

Now we can add the code to complete our task. To understand how this solution works, it is necessary to understand how the page is executed.

Key points:

  • The page is executed from the outer most control inside the code.

  • Each control calls the methods in its child controls. For example, the orders table control row DataBind method calls the OrderDetails table control DataBind method.
The OrdersTableControlRow Class:
Public Class OrdersTableControlRow
    Inherits BaseOrdersTableControlRow
 
    ' Current OrderId Will host the unique server ID of the literal control
    Public Shared CurrentOrderID As String
 
    Public Overrides Sub DataBind()
           CurrentOrderID = Me.Literal.UniqueID
           MyBase.DataBind()
        Dim OrderDetailsTableControl As Order_DetailsTableControl = _
           CType(Me.Page.FindControl("Order_DetailsTableControl"), Order_DetailsTableControl)
        Dim OrderID As String = Literal.Text
        Literal.Visible = False
        If Not IsNothing(OrderDetailsTableControl) Then
             OrderDetailsTableControl.DataChanged = True
             OrderDetailsTableControl.LoadData()
        End If
 
    End sub
 
End Class

CurrentOrderID is initialized by the value of the unique server ID of the literal control that is storing the orderID field value. This is done BEFORE the call to the DataBind, since DataBind will also initialize the OrderDetails table control. A second call to the OrderDetails table control LoadData method is needed to make sure the values are linked to the current order ID. This is done after the values of the row have been set.

The OrderDetailsTableControl Class:
Public Class Order_DetailsTableControl        Inherits BaseOrder_DetailsTableControl
 
       Public Shared CurrentReservationControlID As String
 
       Public Overrides Function CreateWhereClause() As WhereClause
          Dim WC As WhereClause = New WhereClause
          Dim ThisLiteralId As String = _
                NestedEditTables.UI.Controls.EditOrders.OrdersTableControlRow.CurrentOrderID
    ' Get the literal control of the order id
          Dim CurrentLiteral As Literal = _
                CType(Me.Page.FindControlRecursively(ThisLiteralId), Literal)
          If Me.Page.IsPostBack Then
             WC.iAND(Order_DetailsTable.OrderID, BaseFilter.ComparisonOperator.EqualsTo, _
                 Me.ReservationIdLabel.Text)
          Else
    ' If control is found - add the orderid to the where clause
             If Not IsNothing(CurrentLiteral) Then
                WC.iAND(Order_DetailsTable.OrderID, BaseFilter.ComparisonOperator.EqualsTo, _
                     CurrentLiteral.Text)
             Else
                WC.iAND("1=0")
             End If
          End If
          Return WC
       End Function
 
       Public Overrides Sub SaveData()
          CurrentReservationControlID = ReservationIdLabel.UniqueID
          MyBase.SaveData()
       End Sub
 
       Public Overrides Sub LoadData()
          ' Set the unique server id of the label control
          CurrentReservationControlID = ReservationIdLabel.UniqueID
          Dim ThisLiteralId As String = _
                NestedEditTables.UI.Controls.EditOrders.OrdersTableControlRow.CurrentOrderID
          Dim CurrentLiteral As Literal = CType(Me.Page.FindControlRecursively(ThisLiteralId), _
                Literal)
          If Not Me.Page.IsPostBack Then
             ' Set the value of the order id in the table control label for use of new records
             If Not IsNothing(CurrentLiteral) Then
                 Me.ReservationIdLabel.Text = CurrentLiteral.Text
             End If
             Me.ReservationIdLabel.Visible = False
          Else
             If Not IsNumeric(Me.ReservationIdLabel.Text) Then
                 If Not IsNothing(CurrentLiteral) Then
                   Me.ReservationIdLabel.Text = CurrentLiteral.Text
                 End If
                 Me.ReservationIdLabel.Visible = False
             End If
          End If
          MyBase.LoadData()
       End Sub
 
End Class

Set the where clause to match the relevant order ID, the row that the Edit Table control is displayed in. To do this, use the literal we set the value for in the orderstablecontrolrow class. Set the value of the ReservationIdLabel in the LoadData method. This serves as the container for the default OrderID field in newly created records.

The Order_DetailsTableControlRow Class:
Public Class Order_DetailsTableControlRow
    Inherits BaseOrder_DetailsTableControlRow
 
    Public Overrides Sub DataBind()
        MyBase.DataBind()
        Dim ThisRowId As String
        Dim ThisLiteralId As String = _
        NestedEditTables.UI.Controls.EditOrders.Order_DetailsTableControl.CurrentReservationControlID
        Dim CurrentLiteral As Label = CType(Me.Page.FindControlRecursively(ThisLiteralId), Label)
        If Me.IsNewRecord Then
           If Not IsNothing(CurrentLiteral) Then
              Me.ReservationID.Text = CurrentLiteral.Text
           Else
              Me.ReservationID.Text = "0"
           End If
        End If
End Sub
 
End Class

Set the value of the Order ID in the ReservationID, using the literal control used when a new record is created. This value is taken from the ReservationIdLabel control that was assigned in the DataLoad method of the orderdetailsTableControl class.

Final Code Customization:
Public Class Order_DetailsTableControlRow
    Inherits BaseOrder_DetailsTableControlRow
 
    Public Overrides Sub DataBind()
        MyBase.DataBind()
        Dim ThisRowId As String
        Dim ThisLiteralId As String =
        NestedEditTables.UI.Controls.EditOrders.Order_DetailsTableControl.CurrentReservationControlID
        Dim CurrentLiteral As Label
        = CType(Me.Page.FindControlRecursively(ThisLiteralId), Label)
        If Me.IsNewRecord Then
           If Not IsNothing(CurrentLiteral) Then
              Me.ReservationID.Text = CurrentLiteral.Text
           Else
              Me.ReservationID.Text = "0"
           End If
        End If
    End Sub
 
End Class
 
 
Public Class Order_DetailsTableControl
    Inherits BaseOrder_DetailsTableControl
 
    Public Shared CurrentReservationControlID As String
 
    Public Overrides Function CreateWhereClause() As WhereClause
        Dim WC As WhereClause = New WhereClause
        Dim ThisLiteralId As String =
        NestedEditTables.UI.Controls.EditOrders.OrdersTableControlRow.CurrentOrderID
        ' Get the literal control of the order id
        Dim CurrentLiteral As Literal = CType(Me.Page.FindControlRecursively(ThisLiteralId), Literal)
        If Me.Page.IsPostBack Then
           WC.iAND(Order_DetailsTable.OrderID, BaseFilter.ComparisonOperator.EqualsTo,
           Me.ReservationIdLabel.Text)
        Else
           ' If control is found - add the orderid to the where clause
           If Not IsNothing(CurrentLiteral) Then
              WC.iAND(Order_DetailsTable.OrderID, BaseFilter.ComparisonOperator.EqualsTo,
              CurrentLiteral.Text)
           Else
              WC.iAND("1=0")
           End If
        End If
        Return WC
    End Function
 
    Public Overrides Sub SaveData()
        CurrentReservationControlID = ReservationIdLabel.UniqueID
        MyBase.SaveData()
    End Sub
 
    Public Overrides Sub LoadData()
        ' Set the unique server id of the label control
        CurrentReservationControlID = ReservationIdLabel.UniqueID
        Dim ThisLiteralId As String =
        NestedEditTables.UI.Controls.EditOrders.OrdersTableControlRow.CurrentOrderID
        Dim CurrentLiteral As Literal = CType(Me.Page.FindControlRecursively(ThisLiteralId), Literal)
        If Not Me.Page.IsPostBack Then
           ' Set the value of the order id in the table control label for use of new records
           If Not IsNothing(CurrentLiteral) Then
              Me.ReservationIdLabel.Text = CurrentLiteral.Text
           End If
           Me.ReservationIdLabel.Visible = False
        Else
           If Not IsNumeric(Me.ReservationIdLabel.Text) Then
              If Not IsNothing(CurrentLiteral) Then
                 Me.ReservationIdLabel.Text = CurrentLiteral.Text
              End If
              Me.ReservationIdLabel.Visible = False
           End If
        End If
        MyBase.LoadData()
    End Sub
 
End Class
 
 
Public Class OrdersTableControlRow
    Inherits BaseOrdersTableControlRow
    ' The BaseOrdersTableControlRow implements code for a ROW within the
    ' the OrdersTableControl table. The BaseOrdersTableControlRow implements the DataBind and
    SaveData methods.
    ' The loading of data is actually performed by the LoadData method in the base class of
    OrdersTableControl.
    Public Shared CurrentOrderID As String
    ' This is the ideal place to add your code customizations. For example, you can override the
    DataBind,
    ' SaveData, GetUIData, and Validate methods.
    Public Overrides Sub DataBind()
        CurrentOrderID = Me.Literal.UniqueID
        MyBase.DataBind()
        Dim OrderDetailsTableControl As Order_DetailsTableControl = CType(Me.Page.FindControl
        ("Order_DetailsTableControl"),Order_DetailsTableControl)
        Dim OrderID As String = Literal.Text
        Literal.Visible = False
        If Not IsNothing(OrderDetailsTableControl) Then
              OrderDetailsTableControl.DataChanged = True
              OrderDetailsTableControl.LoadData()
        End If
 
    End sub
 
End Class
 
 
Public Class OrdersTableControl
    Inherits BaseOrdersTableControl
 
    ' The BaseOrdersTableControl class implements the LoadData, DataBind, CreateWhereClause
    ' and other methods to load and display the data in a table control.
 
    ' This is the ideal place to add your code customizations. You can override the LoadData and
    CreateWhereClause,
    ' The OrdersTableControlRow class offers another place where you can customize
    ' the DataBind, GetUIData, SaveData and Validate methods specific to each row displayed on
    the table.
 
    Public Overrides Sub SaveData()
        Dim DisplayedTableControls() As OrdersTableControlRow = Me.GetRecordControls
        Dim SingleReservationControlRow As OrdersTableControlRow
        '         ' For each Row - save the Details data
        '         For Each SingleReservationControlRow In DisplayedTableControls
              SingleReservationControlRow.Order_DetailsTableControl.SaveData()
        Next
        MyBase.SaveData()
    End Sub
 
 
End Class

Now we have nested Edit Table controls that are linked to one another. Please review my sample project for a better understanding at: http://sjc.ironspeed.com/tool/post/ironspeed/vpost?id=1983913&pid=18758812#post18758812 . Feel free to experiment with it yourself!

About the Author
Gil Givati
CEO of Efficens Software Ltd.

Gil has been working with IT systems for the past 17 years from both the infrastructure side of applications and the development side of it. During these years he has served as a database systems team leader for one of the Israeli defense forces software units and Chief technology officer. Gil has also served as the products group manager and CTO in a software house that is representing Sybase Inc., iAnywhere Solutions, Information Builders and other companies. As part of his job he was required to take active part in systems design and deployment. For the past two years Gil has managed Efficens Software and while also taking active part in its projects and products activities.

Gil earned a BA in Business Management from the Derby University in the United Kingdom.



  Privacy Statement