- Description
- UI Samples
- How to use it
- Basic
- Building the basic structure of the TreeGrid in a screen
- Functionality of adding new children to TreeGrid
- Designing frames for each node type
- Backend logic for the TreeGrid
- Creating new root nodes (root-structs)
- Extended
- Override the existing TreeGridEvents
- Creating a TreeNode directly
- Add more columns to your Tree View
- Subscribe to the events of TreeGrid in backend
- Using the functionality of the StructHelper
- Attributes
Description
This section describes the use of the object SimpleSwatTreeGrid, a possibility to show and work on nested (recursive) data structures.
Some theoretical introduction: The TreeGrid is a complex object type, which combines the nested structure of a tree and the display of multiple columns per entry, which is provided by a grid. To enable such Tree-Structures (which means parent-child-relations), B1 provides a generic datamodel, named Struct-Tables, which hold the relation information (by using the selfHdl). The needed logic (which data should be displayed, which types can have relations) can be achieved by configurations and some mapping in OE Code.
UI Samples
Here you’ll find some screenshots of the TreeGrid in action.
Screen Designer:
Form Designer:
Organisation:
Proposal:
How to use it
Afterwards you’ll find the different topics and possibilities you’ll have to individualise your TreeGrid
Basic
Building the basic structure of the TreeGrid in a screen
- Add a copy of the
SimpleSwatTreeGrid, e.g.YourTreeGridto your screen - Add as primary DSO the DSO of your head of the TreeGrid (also known as the root node) to your screen
- Connect
YourTreeGridand the DSO via data-link - Fill the following attributes of the TreeGrid:
EntityName = eStruct- ResourceName: Fill in the name of the resource as file structure, starting with the folder below
src/backendof the Progress Class, which defines this TreeGrid - e.g.
ResourceName = customer.offer.struct.OfferStructEntity) - To add tree entries and maintain the TreeGrid you need a
SimpleSwatRibbonin your screen - In the
YourTreeGridyou have to add the fields, which you want to display there. Therefore go to the tabinstancesand add them there. Please have in mind, that in the Tree only fields from yourStructEntitywill be displayed. How to add there individual columns, please see.
ℹ️ To be faster we recommend to use the Template Tree Window (simple)
Add functionality to the TreeGrid with a ribbon menu structure to the screen
- The
SimpleSwatRibbonmust be connect (at least) with yourTreeGrid - a link with link-type
Toolbarand - a link with link-type
User1 - Take the
GenericStructTreeRibbonmenu structure to the followingSimpleSwatRibbonAttribute: ToolBarKey = GenericStructTreeRibbon
Functionality of adding new children to TreeGrid
- Every TreeGrid can have different types of records, e.g. group-, text- or item-positions. You configure the types via the content types (see chapter below): Name, icon and colour, related DB-table, and the screen which should be shown in the right panel, when selecting a child of the TreeGrid
- Configuration:
- In the menu structure
GenericStructTreeRibbonthere is a dropdown included to add new records of specific types to the TreeGrid which you can use. - To add new options to the DropDown, use the
typeRangeattribute of theSimpleSwatTreeGrid. - Here you can specify all
Content Typesyou want to display, e.g. if you would like to display theContent Typespos.ofr.positionandpos.ofr.textjust usepos.ofr. as the value oftypeRange. Keep in mind, that all content types will be shown, which starting with the string pos.ofr.
Add Content Types
Content Typesare used in this case to define the types of different tree nodes. Following information are relevant when creating a new oneKennung(Key): The name of the type, please use the same beginning for one menu structureZiel Table ID(Target Table ID): Add here the B1 Table ID for the corresponding table. This is needed to enable the copy via drag&dropStamm Table ID(Source Table ID): TBDBezeichnung(Name): Name of the type which will be displayed in the dropdownVerschiedenes(Others): Name of the frame which will be displayed in the right panel when selecting an entry in the TreeGridBild Adresse(Image/Icon): Name of the font awesome icon for that entryMögliche Ziel-Kennung(Target Key): Add here theKennungof the content types, into which you want to allow to copy something. This is needed to enable the copy via drag&dropMögliche Kopf-Kennung(Source Key): TBD
While you can define the content types for the childs in any way, please be aware that the name of the root node of the tree grid must start with pos.own.
Designing frames for each node type
When choosing a node from the treegrid, you can display the details on the right side of the tree (e.g. in a form). Therefore you have to design own frames (like windows) and connect them to your node type. For each type you can display a different frame. The design of a frame will be done in the same way as a window, so you can add DSOs, Grids and Forms to the frame. Keep in mind to set the foreignKey attribute of the PrimaryDSO of the frame with the foreign keys of the root and the nodes
Define the corresponding frame for each node type:
- In the Content types desktop you can define the corresponding frame by copying the name of the frame in the column
VerschiedenesorMisc
Special fields for the frame
- You also have to add (or create them new) some special fields to the entity, which is included in the primaryDSO of your frame
akStructParentHdl,akStructTypeHdl,akStructRelationType,refHdl
Backend logic for the TreeGrid
To make the TreeGrid working, you need to add the following backend logic. In the following chapters we are using yourTree as an example/placeholder for the Tree you’re creating
Include dataset for your DSO
You need to create a yourTree.i file for the dataset-definition of your entity. To do that go in the B1 UI to Integrate -> Data -> Datasource -> Generate Dataset-Definition. Choose your DSO and copy the whole output in the src/backend/yourTree/yourTree.i
EventHandler for TreeRoot
Add this e.g. into src/backend/yourTree/YourTreeEventHandler.cls. In the DSO of YourTreeEntity, add this eventHandler as attribute ServerEventsHandler
BLOCK-LEVEL ON ERROR UNDO, THROW.
CLASS yourTree.YourTreeEventHandler
INHERITS Akioma.Swat.OERA.Dynamic.BaseServerEventHandler:
{ yourTree/yourTree.i &ACCESS="PRIVATE" }
CONSTRUCTOR YourTreeEventHandler():
SUPER(DATASET dsData:HANDLE).
THIS-OBJECT:RegisterHandler(1, NEW Akioma.Swat.OERA.Dynamic.Strategy.Struct.StructOwnerHandler("pos.own.yourtree")).
END CONSTRUCTOR.
END CLASS.StructDataAccess
Add this to src/backend/yourTree/Struct/YourTreeStructDataAccess.cls
StructEntity
Add this to src/backend/yourTree/Struct/YourTreeStructEntity.cls
Include dataset for the struct
Add this to src/backend/yourTree/Struct/dsStruct.i
&SCOPED-DEFINE ACCESS {&ACCESS}
&SCOPED-DEFINE REFERENCE-ONLY {&REFERENCE-ONLY}
&SCOPED-DEFINE SUFFIX {&SUFFIX}
&GLOBAL-DEFINE DATASET-NAME dsStruct
{ yourTree/Struct/eStruct.i }
DEFINE {&ACCESS} DATASET dsStruct{&SUFFIX} {&REFERENCE-ONLY} FOR eStruct{&SUFFIX}.Add this to src/backend/yourTree/Struct/eStruct.i. In this file you can add additional fields to your Struct Business Entity
DEFINE {&ACCESS} TEMP-TABLE eStruct{&SUFFIX} NO-UNDO {&REFERENCE-ONLY} &IF DEFINED (NO-BEFORE) EQ 0 &THEN BEFORE-TABLE eStructBefore{&SUFFIX} &ENDIF
{Akioma/Swat/Struct/base-struct-standard-fields.i}
INDEX iScan AS PRIMARY UNIQUE ParentHdl SeqNum.EventHandler for TreeNodes
Add this here src/backend/yourTree/Struct/Nodes/TreeNodeEventHandler.cls. In the DSO of the corresponding TreeNodeEntites, add this eventHandler as attribute ServerEventsHandler
BLOCK-LEVEL ON ERROR UNDO, THROW.
CLASS yourTree.Struct.Nodes.TreeNodeEventHandler
INHERITS Akioma.Swat.OERA.Dynamic.BaseServerEventHandler:
CONSTRUCTOR TreeNodeEventHandler():
THIS-OBJECT:RegisterHandler(1, NEW Akioma.Swat.OERA.Dynamic.Strategy.Struct.StructNodeHandler()).
END CONSTRUCTOR.
END CLASS.Creating new root nodes (root-structs)
Within your treeGrid-Object (document, organisation etc.) you have to create a new Struct root-node each time you create a new object.
For the root-node you need a content type pos.own.YourObjectRootContentType (starting with pos.own is mandatory)
For the addStruct-Method the following parameters are available:
- Hdl of the parent or previous node
- Type of the node (this is the SelfHdl of the content type you want to add)
- Relation of newly created node as integer, possible values:
- stRelNextSibling -> 1
- stRelFirstChild -> 3
- stRelRootNode -> 4
- Optional: The Hdl of the data you want to connect to the created node
Extended
Override the existing TreeGridEvents
The Struct business entities have the following overridable events:
Method | Description | Name |
GetNewStructHdl | called when triggering the create event in the treegrid, it will return a Struct hdl for the struct that will be created | METHOD PUBLIC VOID GetNewStructHdl(INPUT-OUTPUT DATASET-HANDLE phDataset, poStructHdl AS Consultingwerk.CharacterHolder). |
GetChainToRoot | called when positioning in the tree at a specific record, it returns a comma-separated list of Struct hdls required to be expanded to be able to reach the record from the root | METHOD PUBLIC VOID GetChainToRoot(INPUT-OUTPUT DATASET-HANDLE phDataset, poRecordId AS Consultingwerk.CharacterHolder). |
CopyStructRecords | called when triggering a copy in the treegrid | METHOD PUBLIC VOID CopyStructRecords(INPUT-OUTPUT DATASET-HANDLE phDataset, poParameter AS Akioma.Swat.Struct.OperationStructRecordsParameter). |
MoveStructRecords | called when triggering a move in the treegrid | METHOD PUBLIC VOID MoveStructRecords(INPUT-OUTPUT DATASET-HANDLE phDataset, poParameter AS Akioma.Swat.Struct.OperationStructRecordsParameter). |
LinkStructRecords | called when linking struct records | METHOD PUBLIC VOID LinkStructRecords(INPUT-OUTPUT DATASET-HANDLE phDataset, poParameter AS Akioma.Swat.Struct.OperationStructRecordsParameter). |
DeleteStructRecords | called when triggering a delete in the treegrid | METHOD PUBLIC VOID DeleteStructRecords(INPUT-OUTPUT DATASET-HANDLE phDataset, poParameter AS Akioma.Swat.Struct.OperationStructRecordsParameter). |
SetStructFlag | called when setting the struct flag on a treegrid entry | METHOD PUBLIC VOID SetStructFlag(INPUT-OUTPUT DATASET-HANDLE phDataset, poParameter AS Akioma.Swat.Struct.SetStructFlagParameter). |
GetStructFlag | called when fetching the struct flag on a treegrid entry | METHOD PUBLIC VOID GetStructFlag(INPUT-OUTPUT DATASET-HANDLE phDataset, poParameter AS Consultingwerk.CharacterHolder). |
GetDragAndDropDialog | called when doing a drag-and-drop in the treegrid, returns the name of the dialog which should be launched | METHOD PUBLIC VOID GetDragAndDropDialog(INPUT-OUTPUT DATASET-HANDLE phDataset, poParameter AS Akioma.Swat.OERA.GetDragAndDropDialogParameter). |
These are the properties of Akioma.Swat.Struct.OperationStructRecordsParameter:
These are the properties of Akioma.Swat.OERA.GetDragAndDropDialogParameter:
These are the properties of Akioma.Swat.Struct.SetStructFlagParameter:
{Consultingwerk/JsonSerializableProperty.i StructHdl CHARACTER}.
{Consultingwerk/JsonSerializableProperty.i Flag CHARACTER}.Creating a TreeNode directly
Implement a function, which creates one TreeNode directly (can be included in the Ribbon instead of the dropdown):
export default function createNodeByType(eventSource: akioma.swat.Ribbon, cType: string, iRelation: number): void {
const oTreeGrid = eventSource.getLink('USER1:TARGET');
oTreeGrid.controller.storeConnector.tree.typeOfAdd = cType;
oTreeGrid.controller.storeConnector.createNewNode(eventSource, iRelation);
}- As nodeType you have to hand over the selfHdl of the nodeType.
- How-to: You can use e.g. the Developer Tools of the Chrome Browser, open the content types desktop and there you will find the selfHdl of each nodeType
Add more columns to your Tree View
If you want to display more columns in your Tree you have to add the necessary fields to your StructEntity. There are two options to do this:
- Implement this directly in the
structEntity.clsin theAfterFetchevent with your individual logic. With this approach it’s also possible to calculate some fields - Map the fields from some application entity to the
StructEntity. In this context you can also add additional fields to theStructEntity - To add new fields, e.g.
CustomField1, to theStructEntityyou have to add them intosrc/backend/yourTree/Struct/eStruct.i, see example - To map the fields, add these mappings from your source entity to the
StructEntityin theStructDataAccesssee here:TreeGrid - StructDataAccess
- After mapping these, add also a field in the B1 UI to
YourTreeGrid.Therefore add it in the tabInstancesand please use the same name as you defined in theeStruct.ifile
DEFINE {&ACCESS} TEMP-TABLE eStruct{&SUFFIX} NO-UNDO {&REFERENCE-ONLY} &IF DEFINED (NO-BEFORE) EQ 0 &THEN BEFORE-TABLE eStructBefore{&SUFFIX} &ENDIF
{Akioma/Swat/Struct/base-struct-standard-fields.i}
FIELD CustomField1 AS CHARACTER
FIELD CustomField2 AS INTEGER
INDEX iScan AS PRIMARY UNIQUE ParentHdl SeqNum.Subscribe to the events of TreeGrid in backend
Please see here: Events: Subscribing to Backend-Events
Using the functionality of the StructHelper
To check in your OpenEdge code the position in your grid, you can use the following functionalities of the StructHelper class:
- Get a chain: Owner,Root,node1,node2,....,<parent of chNode>
Akioma.Swat.Struct.StructHelper:reqChainToRoot(INPUT pcNodeHdl AS CHARACTER) - Receives an Owner-Hdl and sets the QueryPrepare to the top of that tree, that is the one record that has this owner and has no parent.
Akioma.Swat.Struct.StructHelper:reqFirstOfOwner(INPUT pcOwnerHdl AS CHARACTER) - Returns the the SelfHdl of next sibling of a given record
Akioma.Swat.Struct.StructHelper:reqStructHdlAfter(INPUT pcHdl AS CHARACTER) - Returns the SelfHdl of the previous sibling of a given record
Akioma.Swat.Struct.StructHelper:reqStructHdlBefore(INPUT pcHdl AS CHARACTER) - Returns the SelfHdl of the child record of a given record
Akioma.Swat.Struct.StructHelper:reqStructHdlChild(INPUT pcHdl AS CHARACTER)
Attributes
attribute | description | Example/Values |
SUBTYPE | Defines, how the TreeGrid will be rendered | - SKIP_LISTBAR: will render only the treegrid and posframe
- TREE-ONLY: will only render the treegrid itself (no listbar, no posframe) |
Back to Documentation
Back to Home Page