Groups of Groups for table editing

Hi,

I’m trying to add a table in the diagram where user can add header-columns and content-columns.
I thought that I can use a group-template where the group is divided into 2 subgroups, one on the left half and one on the right half where I can drop nodes into for header and content.
I followed the “Regrouping Demo” to use groups to do the work, changing the “OfGroups” definition to have that group already divided into 2 sub groups, but I cannot succed in.

Is that the right way to do this or can you tell me how to proceed?

I also tried to use table panel to to that but it seems to me that is more difficult to handle the row size and change it dynamically.

Many thanks in advance!
Andre

Do you have a screenshot or sketch of what you are looking for? What exactly are you unable to do and what did you try?

Hi,
I added a node in the palette with category set as “Table” and isGroup=true and then I added a template for table group:

myDiagram.groupTemplateMap.add(“Table”,
$$(go.Group, go.Panel.Auto,
{
background: “transparent”,
// highlight when dragging into the Group
mouseDragEnter: function (e, grp, prev) { highlightGroup(e, grp, true); },
mouseDragLeave: function (e, grp, next) { highlightGroup(e, grp, false); },
computesBoundsAfterDrag: true,
locationSpot: new go.Spot(0, 20, cellSize.width / 2, cellSize.height / 2),
minSize: groupSize,
// when the selection is dropped into a Group, add the selected Parts into that Group;
// if it fails, cancel the tool, rolling back any changes
mouseDrop: finishDrop
},
$$(go.Panel, go.Panel.Vertical,
{ minSize: groupSize },
$$(go.Panel, go.Panel.Horizontal,
{
// stretch: go.GraphObject.Horizontal,
background: “orange”, minSize: new go.Size(200,10)
},
$$(go.TextBlock,
{
alignment: go.Spot.Left, editable: false,
margin: 2, wrap: go.TextBlock.None
},
new go.Binding(“text”, “text”))
), // end Horizontal Panel
//Panel for header column

      $$(go.Placeholder,
        { padding: 0 })
    ),  // end Vertical Panel
    $$(go.Shape, "RoundedRectangle",
      {
          isPanelMain: true,  // the RoundedRectangle Shape is in front of the Vertical Panel
          fill: null,
          spot1: new go.Spot(0, 0, 1, 1),   // the Vertical Panel is placed
          spot2: new go.Spot(1, 1, -1, -1)  // slightly inside the rounded rectangle
      }),
      $$(go.Group, go.Panel.Auto,
    {
        background: "transparent",
        // highlight when dragging into the Group
        mouseDragEnter: function (e, grp, prev) { highlightGroup(e, grp, true); },
        mouseDragLeave: function (e, grp, next) { highlightGroup(e, grp, false); },
        computesBoundsAfterDrag: true,
        locationSpot: new go.Spot(0, 20, cellSize.width / 2, cellSize.height / 2),
        minSize: headerSize,
        // when the selection is dropped into a Group, add the selected Parts into that Group;
        // if it fails, cancel the tool, rolling back any changes
        mouseDrop: finishDrop,
        // Groups containing Groups lay out their members horizontally
        layout:
          $$(go.GridLayout,
            {
                wrappingWidth: Infinity, alignment: go.GridLayout.Position,
                cellSize: new go.Size(50, 50)//, spacing: new go.Size(4, 4)
            })
    })
    ));

the posted code is just one of my test to add subgroups directly in the group definition.
what I’m trying to do is a table like the following image:

Left half is header definition and righ half is content definition.
In this way user can decide the table definition and then I can create that table from server side.
I also looked to the swimlane examples but each lane is a group.

If you have a better way to let user decide column header and content please let me know.

Many thanks.

I would try using a Table Panel. You might want to start with something like in http://gojs.net/latest/samples/entityRelationship.html or http://gojs.net/latest/samples/records.html .

You might also find http://gojs.net/latest/samples/dragDropFields.html very interesting too.

For the vertical separator line, I assume you know about the RowColumnDefinition.separator… properties.

Thanks,

I have done the table template with the table panel with 2 columns and it’s working.

var tableTemplate =
$$(go.Node, “Auto”,
{
movable: true,
copyable: false,
deletable: false,
resizable: true,
defaultStretch: go.GraphObject.Fill,
background: “transparent”,
locationSpot: new go.Spot(0, 0, cellSize.width / 2, cellSize.height / 2),
minSize: groupSize,
},
new go.Binding(“location”, “loc”, go.Point.parse).makeTwoWay(go.Point.stringify),
// this rectangular shape surrounds the content of the node
$$(go.Shape,
{ fill: “#EEEEEE” }),
// the content consists of a header and a list of items
$$(go.Panel, “Vertical”,
// this is the header for the whole node
$$(go.Panel, “Auto”,
{ stretch: go.GraphObject.Fill }, // as wide as the whole node
$$(go.Shape,
{ fill: “#1570A6”, stroke: null }),
$$(go.TextBlock,
{
alignment: go.Spot.Center,
margin: 3,
stroke: “white”,
textAlign: “center”,
font: “bold 12pt sans-serif”
},
new go.Binding(“text”, “key”))),
// this Panel holds a Panel for each item object in the itemArray;
// each item Panel is defined by the itemTemplate to be a TableRow in this Table
$$(go.Panel, “Table”,
{
name: “TABLE”,
padding: 0,
//minSize: tableSize,
defaultStretch: go.GraphObject.Fill,
defaultRowSeparatorStroke: “black”,
defaultColumnSeparatorStroke: “black”,

                    //itemTemplate: fieldTemplate
                },
                    $$(go.RowColumnDefinition, { row: 1, height: 50 }),
                    //Define the Column for headers
                    $$(go.Panel, "TableRow", { row: 0 },
                        $$(go.TextBlock, "Header",
                            { column: 0, height: 25, width: 100,font: "bold 10pt sans-serif", margin: 0 }),
                    

                    $$(go.TextBlock, "Content",
                    { column: 1, height: 25, width: 100, font: "bold 10pt sans-serif", margin: 0 })),
                    
                   
                $$(go.Panel, "TableRow", { row: 1},
                    ////////////////////
                    //HEADER
                    //////////////////////
                    $$(go.Panel, "Vertical",
                        { column: 0  }
                    ),

                    ////////////////////
                    //CONTENT
                    //////////////////////
                    $$(go.Panel, "Vertical",
                    { column: 1 }
                    )
                ),

                    
              $$(go.RowColumnDefinition, { row: 0, separatorStrokeWidth: 1.5, separatorStroke: "black" }),
              $$(go.RowColumnDefinition, { column: 1, separatorStrokeWidth: 1.5, separatorStroke: "black" })
             
            )  // end Table Panel of items
          )  // end Vertical Panel
        );  // end Node

Now I have to drag fields from the palette and also from the diagram and drop them into one column of the table (header or content). For this purpose I created a custom dragging tool class and assigned to the diagram, overriding the same method as in the Regrouping Demo (findDraggablePart, doActivate, doDeactivate, doMouseMove, doMouseUp). In this case the dragging tool is called only when I drag from inside the diagram and not from the palette. How can I drag nodes directly from the palette into the table?

Many thaks for your precious help.

Andre

Wait – surely you meant to say that you copied the custom DraggingTool from the DragDropFields sample, not from the Regrouping sample which does not have a custom DraggingTool, yes?

Basically do you want each half of your node to have its own list of items? If so, you want to use TWO Panels each with its own Binding of Panel.itemArray to a data property that is a JavaScript Array. Presumably one Panel is in column 0 of your Table Panel, and the other one is in column 1.

It appears that that is your intent with the two Vertical Panels commented “//HEADER” and “//CONTENT”. So set one with a Binding of “itemArray” to “headers” and the other to “content”, which you set up as Array-valued properties on your node data. You’ll also want to set each Panel’s itemTemplate too. Instances of that item template will be created and bound to each item in the respective Array.

You also will need to modify the doMouseUp code to use the InputEvent.documentPoint to find out which Vertical Panel that that point is within, so you can decide which Array to modify.

Yes sorry, I copied the dragging tool from DragDropFields.
And yes you are correct also for what I’m trying to do with the two Vertical Panels.

I saw that the doMouseUp event only fires if I start to drag from inside the diagram and not fires if I start to drag from the palette.
In this case I saw that the only event fired while drag/drop from the palette is the doDropOnto. Do I need to override and use this event or am I going wrong?

Many thanks.

The DragDropFields sample has a custom DraggingTool so that it can start dragging an item from a Panel and so that it can drop an item at a particular position in the target list. If you do not need to do either of these things, then I suspect you do not need the custom DraggingTool at all. Just implement a mouseDrop event handler on each of your Vertical Panels. The second argument will be that Panel, so that is how you will know which model Array to add to. And that event handler will be called both for internal as well as for external drag-and-drops.

Yes, you could override doDropOnto if you want to change that behavior, but I’m hoping that you won’t need to.

Ok, great.
But the mouseDrop event is only fired ad the node level and not at panel level.
Starting from vertical panel for HEADER/CONTENT I’ve added the mouseDrop event with an alert message and tested it.
But the alert message came only when I added it at the node level.

var tableTemplate =
$$(go.Node, “Auto”,
{
[…]
mouseDrop: function (inputEvent, graphObject) {
var diagram = graphObject.diagram;
alert(“event node”);
}
},
[…]
// the content consists of a header and a list of items
$$(go.Panel, “Vertical”, {
mouseDrop: function (inputEvent, graphObject) {
var diagram = graphObject.diagram;
alert(“event panel”);
}
},
[…]
$$(go.Panel, “Table”,
{
[…]
mouseDrop: function (inputEvent, graphObject) {
var diagram = graphObject.diagram;
alert(“event table”);
}
//itemTemplate: fieldTemplate
},
[…define row 0 for labels…]
$$(go.Panel, “TableRow”,
{
row: 1,
<span =“Apple-tab-span” style=“white-space:pre”> mouseDrop: function (inputEvent, graphObject) {
var diagram = graphObject.diagram;
alert(“event table row”);
}
},
////////////////////
//HEADER
//////////////////////
$$(go.Panel, “Vertical”,
{
column: 0,
mouseDrop: function(inputEvent, graphObject) {
var diagram = graphObject.diagram;
alert(“event header”);
}
},
new go.Binding(“itemArray”, “headerFields”)

                        ),
                     ////////////////////
                    //CONTENT
                    //////////////////////
                        $$(go.Panel, "Vertical",
                            {
                                column: 1},
                            {
                                mouseDrop: function(inputEvent, graphObject) {
                                    var diagram = graphObject.diagram;
                                    alert("event content");
                                }
                            },
                            new go.Binding("itemArray", "contentFields")
                    
                        )
                    ),

                    $$(go.RowColumnDefinition, { row: 0, separatorStrokeWidth: 1.5, separatorStroke: "black" }),
                    $$(go.RowColumnDefinition, { column: 1, separatorStrokeWidth: 1.5, separatorStroke: "black" })
                    //new go.Binding("itemArray", "fields")
                )  // end Table Panel of items
            )  // end Vertical Panel
        );  // end Node

If I’ve not misunderstood your last reply you said to add the mouseDrop event in both my vertical panels.

Thanks for your patience.

You’re definitely on the right track. I suspect that the reason your Vertical Panel mouseDrop handler is not being called is because the Panel has “nothing” there. You could set its background to be “transparent” or some regular color.

Yes, the Panels are initially empty because I will populate them dropping items into.
And even setting the background with transparent or a regular color the event is not fired.

This means that for the first item of Vertical Panels do I need to handle the drop of an item in the mouseDrop event of the whole node?

Many thanks again.

I just tried adding a mouseDrop handler on a Panel inside a Node that also had a mouseDrop handler, and the one on the nested Panel definitely took precedence when the drop point was actually in that Panel. And of course the one for the whole node occurred when the drop was elsewhere in the node.

If you set the Vertical Panel’s background to be “red”, do you see that red panel? Maybe that Vertical Panel is zero-sized until there are some items in it. Try giving it a minSize.

Hi,
even setting the background to red it’s not displayed and even setting the minSize nothing changed.
I tried this with and without the RowColumnDefinition to set the dimension of that row and the only difference is that without the RowColumnDefinition both vertical panel are not sized even with the minSize. Maybe because they are empty.
Here is the piece of code:
[…]
$$(go.RowColumnDefinition, { row: 1, height: 50 }),
[…ROW 0 definition…]

////////////////////
//HEADER
//////////////////////
$$(go.Panel, “Vertical”,
{
column: 0,
background: “red”,
minSize: new go.Size(100, 100),
mouseDrop: function(inputEvent, graphObject) {
var diagram = graphObject.diagram;
alert(“event header”);
}
},
new go.Binding(“itemArray”, “headerFields”)

                        ),

                        ////////////////////
                    //CONTENT
                    //////////////////////
                       $$(go.Panel, "Vertical",
                            {
                                column: 1,
                                background: "red",
                                minSize: new go.Size(100, 100),
                                mouseDrop: function(inputEvent, graphObject) {
                                    var diagram = graphObject.diagram;
                                    alert("event content");
                                }
                            },
                            new go.Binding("itemArray", "contentFields")
                        )

Any suggestion?

Many thanks in advance.
Andre

The minSize seems not to have an effect when there are no objects inside the Panel. If you have some data in your “headerFields” and “contentFields” Arrays, everything looks OK and mouse drops alert appropriately.

We’ll investigate.

By the way, the “TableRow” Panel is completely superfluous, besides alerting on a mouseDrop.

Many thanks, I’ll wait your reply.

Btw, for the moment I use a template for the table with item array for both columns already populated with one element (I use it as a example with the label “drop here”) and handle it in the mouse drop event.
Is there a way to have the vertical panel (header/content) having an itemTemplate as for the table panel? I created a field template and I tried to set the ‘itemTemplate: fieldTemplate’ in both vertical panels, but it seems that the property only work for table panel.

Many thanks again.
Andre

Ok, I solved the template problem changing the 2 Vertical Panels (header/content) with 2 tables and setting the itemTemplate property.

Andre

And we have fixed the bug for the next release.

Great. Do you already know when will it be available, even in beta?
Just one question: I’m implementing the mouse drop event as you suggested me to call the diagram.model.insertArrayItem(graphObject.itemArray … but I can’t figure out how to retrieve the information about item that I’m dropping in the vertical panel.

Can you please help me?

Thank you very much for all your help

Andre

Next week.

In general you can find what was dropped by looking at the Diagram.selection collection of Parts.

But the samples/dragDropFields.html sample has a custom FieldDraggingTool, and when dragging fields the data is held in an internal property of FieldDraggingTool.

Great. Sorry If I continue to write here, but in a new post I would explain again what I’m doiong…but If you prefer I can open a new topic to ask you few more questions.

Now I succeded to add item in both my vertical panel (that now are “Table”, to let me assign the itemTemplate property) and the things I’m trying to fix are:

  1. If I add some editable textblocks in both my vertical panels, I notice that when I click the text to edit it, the text box for editing is displayed with some distance under my item. And this gap increases each time I add an item.

  2. Actually I’m using only the mouseDrop event and not a custom dragging tool. I’m trying to use the click event in vertical panel to emulate the selection of an item (just change the shape color) to let user select and cancel an element. But in this case if I also select another object in the diagram (outside the table) I would like to restore the color of the table item. Is there an easier way to to this instead of cycle all table elements and set default colors?

Many thanks!
Andre