Advanced UI Patterns

While standard Forms and Grids cover 80% of use cases, complex enterprise applications require advanced layout orchestration. Struktural achieves this by linking LayoutNode definitions using UUIDs.

1. Embedded Grids (Master-Detail)

By default, placing a Collection node at the root of a Form's Layout array renders it as a bottom Tab. To embed a grid directly alongside the parent's data (Master-Detail):

UI Approach: Drag a Grid Widget inside a Group node on the canvas. Configure its Target Entity and Filter Field. JSON Approach: Place a Collection node inside the Children array of a Group.

{
  "Type": "Group",
  "Label": "Active Tasks",
  "Children": [
    {
      "Type": "Collection",
      "Label": "Tasks",
      "TargetViewId": "Task_Grid_1",
      "ListProps": {
        "TargetEntity": "ProjectTask",
        "FilterField": "ProjectId",
        "PageSize": 5
      }
    }
  ]
}

2. Tree and Linked Nested Form

For deep hierarchies (e.g., an Organization Chart with Departments and Employees), navigating back and forth between grids and forms loses context. You can render a Tree on the left and a reactive Form on the right.

UI Approach: Add a Tree Widget and a Linked Nested Form side-by-side. In the Tree properties, configure the mappings and select the Nested Form as the target. JSON Approach: You must define two nodes and link them via the LinkedNestedFormUuid.

[
  {
    "_uuid": "tree-pane-uuid",
    "Type": "Tree",
    "ColSpan": 4,
    "TreeProps": {
      "RootEntity": "Department",
      "LinkedNestedFormUuid": "form-pane-uuid", 
      "Mappings": [
        {
          "EntityName": "Department",
          "ChildrenCollectionField": "Employees",
          "TargetFormViewId": "Department_Form_1"
        },
        {
          "EntityName": "Employee",
          "TargetFormViewId": "Employee_Form_1"
        }
      ]
    }
  },
  {
    "_uuid": "form-pane-uuid",
    "Type": "NestedForm",
    "ColSpan": 8
  }
]

4. Conditional Formatting (FormattingRules)

Struktural allows you to dynamically change row or cell colors in Grids, Cards, and Calendars based on data values using Dynamic LINQ expressions.

UI Approach: In the View Editor, locate the Conditional Formatting section (in the main config pane for Grids, or the right-hand Properties pane for Map/Calendar widgets). Click Add Rule, write your Dynamic LINQ expression, and use the color pickers to select the Background and Text colors.

JSON Approach: Add a FormattingRules array to your GridViewDefinition (or Card/Calendar).

"FormattingRules": [
  {
    "Expression": "Status == \"Overdue\" AND DueDate < DateTime.UtcNow",
    "BackgroundColor": "#ffcccc",
    "TextColor": "#990000",
    "Target": "" 
  },
  {
    "Expression": "TotalAmount > 10000",
    "BackgroundColor": "#e6f7ff",
    "Target": "TotalAmount"
  }
]

(Note: If Target is empty, the entire row is colored. If Target is a field name, only that specific column cell is colored.)

5. Configuring Hierarchical RLS UI

When you use Hierarchical Row-Level Security (Materialized ACLs), administrators need a way to assign roles to specific records (e.g., assigning the "Manager" role to a specific "Folder").

This is done by embedding the system entity Struktural_Sys_RecordAcl inside the parent entity's form.

JSON Approach: Add a Collection node to the Form's Layout.

{
  "Type": "Collection",
  "Label": "Permissions",
  "TargetViewId": "RecordAcl_Grid_1",
  "ListProps": {
    "TargetEntity": "Struktural_Sys_RecordAcl",
    "FilterField": "RecordId"
  }
}

3. Reactive Broadcasters (ListenToWidgetUuids)

Sometimes, selecting a row in Grid A should automatically filter the data shown in Grid B on the same screen.

UI Approach: In the Form Canvas, select the target widget (e.g., Grid B). In the right-hand Properties pane, expand Master-Detail Linking. Under "Listen To Selection From", check the boxes for any sibling widgets (e.g., Grid A) that should broadcast their selection state to this widget.

JSON Approach: Add the _uuid of the source widget to the ListenToWidgetUuids array in the target widget's properties.

[
  {
    "_uuid": "source-grid-uuid",
    "Type": "Collection",
    "TargetViewId": "Region_Grid",
    "ListProps": { "TargetEntity": "Region" }
  },
  {
    "_uuid": "target-grid-uuid",
    "Type": "Collection",
    "TargetViewId": "Store_Grid",
    "ListProps": {
      "TargetEntity": "Store",
      "FilterField": "RegionId",
      "ListenToWidgetUuids": [ "source-grid-uuid" ] 
    }
  }
]