Adding a checkbox in a port

Hi ,

I’m trying to use a checkbox like the ones in the “triStateCheckBoxTree” example but just in a port of a group node.
What i did is this (the code for the checkbox is the same as the example)

      myDiagram.groupTemplate =
        $gj(go.Group, "Spot",
          { 
            background: "transparent",
            ungroupable: true,
            // 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,
            // 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,
            handlesDragDropForMembers: true,  // don't need to define handlers on member Nodes and Links
            // Groups containing Groups lay out their members horizontally
            layout: makeLayout(false),
			cursor: "move"
			//dragComputation: stayInGroup

          },
		  new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
          new go.Binding("layout", "horiz", makeLayout),
          new go.Binding("background", "isHighlighted", function(h) {
            return h ? "rgba(0,255,0,0.6)" : "transparent";
          }).ofObject(),
		  $gj(go.Panel, "Auto",
			  $gj(go.Shape, "Rectangle",
				{ 
					fill: null, 
					strokeWidth: 2 },
				new go.Binding("stroke", "horiz", defaultColor),
				new go.Binding("stroke", "color")),
			  $gj(go.Panel, "Vertical",  // title above Placeholder
				$gj(go.Panel, "Horizontal",  // button next to TextBlock
				  {
					name: "hover-screenshot-panel",
					stretch: go.GraphObject.Horizontal, 
					//background: defaultColor(false), 
					portId: "totale",
					// allows links to/from all sides
					fromSpot: go.Spot.Right,
					toSpot: go.Spot.Left,
					fromLinkable: true,
					toLinkable: true,
					cursor:"pointer"
				  },
				  new go.Binding("background", "horiz", defaultColor),
				  new go.Binding("background", "color"),
				  $gj("SubGraphExpanderButton",
					{ alignment: go.Spot.Right, margin: 5 }),
				  $gj(go.TextBlock,
					{
					  alignment: go.Spot.Left,
					  editable: false,
					  margin: 5,
					  font: defaultFont(false),
					  opacity: 0.75,  // allow some color to show through
					  stroke: "#404040"
					},
					new go.Binding("font", "horiz", defaultFont),
					new go.Binding("text", "text").makeTwoWay())
				),	// end Horizontal Panel
				$gj(go.Placeholder,
				  { 
					padding: 15, 
					alignment: go.Spot.TopLeft 
				  })
			  )  // end Vertical Panel
			), // end of Panel Auto
			$gj(go.Panel, "Vertical",{alignment: new go.Spot(1,0.6,40,0) },
				new go.Binding("itemArray", "rightArray"),
				{
				  //row: 3, column: 2,
				  itemTemplate:
					$gj(go.Panel,
					  $("TriStateCheckBoxButton"),
					  {
						_side: "right",
						fromSpot: go.Spot.Right, toSpot: go.Spot.Right,
						fromLinkable: true, toLinkable: false, 
						cursor: "pointer",
						contextMenu: portMenu
					  },
					  new go.Binding("portId", "portId"),
					  $gj(go.Shape, "Rectangle",
						{
						  stroke: null, strokeWidth: 0,
						  desiredSize: portSize,
						  margin: new go.Margin(3, 0),
						  fill: "blue"
						}
					  ),
					  $gj(go.TextBlock,
						{ margin: new go.Margin(5, 0, 0, 5), 
						  font: "bold 12px sans-serif",
						  stroke:"white",
						  alignment: go.Spot.Left, 
						  fromLinkable: false, 
						  toLinkable: false,
						  shadowVisible: false
						  },
						  new go.Binding("text", "portId"))					  
						//new go.Binding("fill", "portColor"))
					)  // end itemTemplate
				}
			 )  // end Vertical Panel
        );

but I get an error loading the page:
“Uncaught Error: Trying to set undefined property “length” on object: Panel([object Object])#769
v …/release/go-Debug.js:12
Mj …/release/go-Debug.js:707
bm …/release/go-Debug.js:1002
$l …/FILL_EM/release/go-Debug.js:996
init …/FILL_EM/js/filler_gojs.js:1524
onload …/cfgparlog/confpar/id/1017:1
go-Debug.js:12:115”
what I’m trying to reach is this
before:
checkboxOLD
after:
checkboxNEW

Is this possible?
Thx

I hope you had modified the “TriStateCheckboxButton” so that it didn’t assume that its state depended on its tree children.

And is the value of data.checked always either true, false, or null? Or else you should change the code to deal with undefined.

ehm…no, I haven’t. The button hasn’t a tree parent, so what I really need is a two state button, true and false, like a classic html checkbutton… The error is related to the third state?

If you don’t need a tri-state checkbox, just use “CheckBox”. You don’t need to include any code from anywhere.

I don’t know what might be causing the exception.

Like this?

    go.GraphObject.defineBuilder("CheckBox", function(args) {
      var button = /** @type {Panel} */ (
        go.GraphObject.make("Button",
          {
            "ButtonBorder.fill": "white",
            width: 14,
            height: 14
          },
          go.GraphObject.make(go.Shape,
            {
              name: "ButtonIcon",
              geometryString: 'M0 0 M0 8.85 L4.9 13.75 16.2 2.45 M16.2 16.2',  // a 'check' mark
              strokeWidth: 2,
              stretch: go.GraphObject.Fill,  // this Shape expands to fill the Button
              geometryStretch: go.GraphObject.Uniform,  // the check mark fills the Shape without distortion
              background: null,
              visible: false  // visible set to false: not checked, unless data.checked is true
            },
            new go.Binding("visible", "checked", function(p) { return p === true || p === null; }),
            new go.Binding("stroke", "checked", function(p) { return p === null ? null : "black"; }),
            new go.Binding("background", "checked", function(p) { return p === null ? "gray" : null; })
          )
        )
      );

The exception depends on this code

You don’t need to define “CheckBox” – it’s predefined by the library.
https://gojs.net/latest/extensions/Checkboxes.html

Great! It works. Just one more question, I didn’t find in example,
how can I check:

  1. the event of click of the checkbox
  2. which checkbox’s been checked

thx

If you want to do some extra actions when the user clicks on a CheckBox, provide a _doClick event handler. There’s an example on that extensions/Checkboxes.html page.

The checked state is implemented as btn.findObject('ButtonIcon').visible. But it’s unusual to care about that – you can just depend on the data property to which that state is bound. You can see the implementation in:
https://gojs.net/latest/extensions/Buttons.js

But is possible to assign a name to the checkbox that is bound from a dinamical assignment?
In that way I can get value from data property as you right suggest

In that Checkboxes.html sample, the first “CheckBox” is declared as:

              $("CheckBox", "choice1",
                $(go.TextBlock, "default")
              ),

So at any time you can look at the node.data.choice1 property on the Node.data object.

If you really care about getting that “CheckBox” Panel of the Node, I suppose you could give that “CheckBox” a GraphObject.name property so that you can find it with node.findObject(...).

I wish to give the same name of the portId to the checkbox, so I guess that by GraphObject.name property you mean that I can name that with a binding like that, is it?:

			              $gj("CheckBox", "",
			                 { "ButtonIcon.stroke": "green",
			            	   "_buttonFillOver": "lightgreen", 
			            	   "_buttonStrokeOver": "green",
			            	   "Button.width": 20, "Button.height": 20,
			            	   margin:new go.Margin(3, 0)
			            	 },
			            	 new go.Binding("name", "portId")
			            	 )

Or the syntax is invalid?
I need to handle the event of checkbox click just to start a recursive function that build ports of node’s parent by checking the true of false of the “port” checkbox, just to be clear…
In the meantime…Happy Easter to you and your team!
F

If that “CheckBox” is in an Panel.itemTemplate, you won’t be able to call Node.findObject with that name on the Node, since each template has its own namespace. The reason is that if there are usually some number of copies of the item template, resulting in duplicate names.

You could call Panel.findItemPanelForData and then call Panel.findObject.

Ah ok, I’ve read now. I’ll make a try next week.
Thx

Ok, but it’s not very clear for me how can I reach that panel from node group… What I know is node key, and each single name of ports and checkboxes (the same) in the itemTemplate. What do you mean by calling Panel.findItemPanelForData ? What data have I to put in to find it for? In the template of the group are defined 2 panels… After this, identified the panel, I will find the Panel containing items, and the findObject method, with the name of the checkbox, will return the checkbox object: the value true or false is accessed as a property?

You do need to find in the Group the Panel that has a Binding on “itemArray”. Then you can call Panel.findItemPanelForData on it, passing it the reference to the Object in that item Array.

Probably I’m missing something… I can reach the panel with

myDiagram.findNodeForKey('-1').findObject('VPANEL')

where “-1” is the key of the group and “VPANEL” is the name of the panel, and this works.
But how can I pass the object reference of the checkbox to the finditemPanelForData?
I have to find within panel with findObject? example :

var panel = myDiagram.findNodeForKey('-1').findObject('VPANEL')
var checkbox=panel.findObject('name of the checkbox')
var cbdata=panel.finditemPanelForData(checkbox)

is that correct?

If you already have a reference to the Panel, you don’t need to call findItemPanelForData. I thought you only had a particular item Object in the Array and wanted to find the corresponding Panel.

well but once find the panel how can I have a reference to the checkbox’s object and properties from the panel? I need to get checkbox value, that is not on the itemArray as far as I can understand. Checkboxes are on panel.data? Or are just a objects like ports? Each checkbox has a unique name in the itemlist (like ports).

Your node data object ought to be something like:

{ key: ..., loc: ..., horiz: ..., color: ..., text: ...,
  rightArray: [ { portId: ..., checked: true }, { portId: ..., checked: false } ],
  ...
}

It sounds like if you just want to get the checked state for a particular port, you can find the information either in the Part or in the model data.

Since the data in the Model should normally be a lot simpler than the Parts/GraphObjects that are in the Diagram, you might want to look in the particular node’s data.rightArray.

If you really want to find it in the Group, you can, but it’s more work and more prone to breakage if you change your templates.

OK this was just what I guessed before you explanation but my node data object after the drag is like this:

{ 
"class": "GraphLinksModel",
  "linkFromPortIdProperty": "fromPort",
  "linkToPortIdProperty": "toPort",
  "nodeDataArray": [ 
{"isGroup":true, "group":"Somma", "size":"100 30", "text":"Somma", "loc":"777 -165.9", "horiz":"#FFAA33", "rightArray":[ {"portId":"Scalo"} ], "key":-1},
{"key":"PE700", "category":"Input", "group":-1, "name":"...", "fields":[ {"id":"PE700_1", "name":"Scalo"} ], "loc":"792.00 -150.90"}
 ],
  "linkDataArray": []}
}

and I wonder why checked: true is not present. The canvas looks like that:
canvas
As you can see the checkbox is just before the textbox of the port but I can’t see it in data.
There’s something I forgot to do?