Perspective Tree - restore tree expansion after self.refreshBinding("props.items")

Ignition V8.1.15

I need to refresh the tree to eliminate the branch if it is now empty or add a new branch if one has been created.
The problem is that the tree collapses so the user has to navigate the tree again.

The onClick event in the refresh button below is intended to refresh the binding and re-expand the selected branch of the two-level tree.

	self.getSibling("Tree").refreshBinding("props.items")
	x = int(self.getSibling("Tree").props.selection[0].split('/')[0])
	self.getSibling("Tree").props.items[x].expanded = True

Tree binding refresh 1

Note that it works if I refresh several times.

Can anyone explain why it doesn’t work on every click?

First thing that comes to my mind is, what is the props.items bound to?

For demo purposes it’s bound to an expression binding null with a script transform to return the items dictionary object.

Binding dictionary object
	true = True
	false = False
	
	return [
	  {
	    "label": "Building 1",
	    "expanded": false,
	    "data": "",
	    "items": [
	      {
	        "label": "Line #13",
	        "expanded": false,
	        "data": {
	          "location": "Building_1",
	          "equipment": "Line #13"
	        },
	        "items": []
	      },
	      {
	        "label": "Line #14",
	        "expanded": false,
	        "data": {
	          "location": "Building_1",
	          "equipment": "Line #14"
	        },
	        "items": []
	      },
	      {
	        "label": "Line #15",
	        "expanded": false,
	        "data": {
	          "location": "Building_1",
	          "equipment": "Line #15"
	        },
	        "items": []
	      },
	      {
	        "label": "Line #17",
	        "expanded": false,
	        "data": {
	          "location": "Building_1",
	          "equipment": "Line #17"
	        },
	        "items": []
	      },
	      {
	        "label": "Line #18",
	        "expanded": false,
	        "data": {
	          "location": "Building_1",
	          "equipment": "Line #18"
	        },
	        "items": []
	      }
	   ]
	  },
	  	  {
	    "label": "Building_2",
	    "expanded": false,
	    "data": "",
	    "items": [
	      {
	        "label": "Line #23",
	        "expanded": false,
	        "data": {
	          "location": "Building_2",
	          "equipment": "Line #23"
	        },
	        "items": []
	      },
	      {
	        "label": "Line #24",
	        "expanded": false,
	        "data": {
	          "location": "Building_2",
	          "equipment": "Line #24"
	        },
	        "items": []
	      },
	      {
	        "label": "Line #25",
	        "expanded": false,
	        "data": {
	          "location": "Building_2",
	          "equipment": "Line #25"
	        },
	        "items": []
	      },
	      {
	        "label": "Line #27",
	        "expanded": false,
	        "data": {
	          "location": "Building_2",
	          "equipment": "Line #27"
	        },
	        "items": []
	      },
	      {
	        "label": "Line #28",
	        "expanded": false,
	        "data": {
	          "location": "Building_2",
	          "equipment": "Line #28"
	        },
	        "items": []
	      }
	   ]
	  }
	]
'''

So the binding data never changes.

If you’d like to test it then create a coordinate view and drop these two items into it.

Tree
[
  {
    "type": "ia.display.tree",
    "version": 0,
    "props": {
      "selection": [
        "1/2"
      ],
      "selectionData": [
        {
          "itemPath": "1/2",
          "value": {
            "equipment": "Line #25",
            "location": "Building_2"
          }
        }
      ],
      "appearance": {
        "expandIcons": {
          "collapsed": {
            "path": "material/arrow_right"
          },
          "expanded": {
            "path": "material/arrow_drop_down"
          }
        },
        "defaultNodeIcons": {
          "expanded": {
            "path": "material/folder_open"
          },
          "collapsed": {
            "path": "material/folder"
          },
          "empty": {
            "path": "material/stop"
          }
        }
      },
      "style": {
        "borderStyle": "solid",
        "borderWidth": 1,
        "borderColor": "#81F68A"
      }
    },
    "meta": {
      "name": "Tree"
    },
    "position": {
      "x": 60,
      "y": 136,
      "height": 200,
      "width": 305
    },
    "custom": {},
    "propConfig": {
      "props.items": {
        "binding": {
          "type": "expr",
          "config": {
            "expression": "null"
          },
          "transforms": [
            {
              "code": "\ttrue = True\n\tfalse = False\n\t\n\treturn [\n\t  {\n\t    \"label\": \"Building 1\",\n\t    \"expanded\": false,\n\t    \"data\": \"\",\n\t    \"items\": [\n\t      {\n\t        \"label\": \"Line #13\",\n\t        \"expanded\": false,\n\t        \"data\": {\n\t          \"location\": \"Building_1\",\n\t          \"equipment\": \"Line #13\"\n\t        },\n\t        \"items\": []\n\t      },\n\t      {\n\t        \"label\": \"Line #14\",\n\t        \"expanded\": false,\n\t        \"data\": {\n\t          \"location\": \"Building_1\",\n\t          \"equipment\": \"Line #14\"\n\t        },\n\t        \"items\": []\n\t      },\n\t      {\n\t        \"label\": \"Line #15\",\n\t        \"expanded\": false,\n\t        \"data\": {\n\t          \"location\": \"Building_1\",\n\t          \"equipment\": \"Line #15\"\n\t        },\n\t        \"items\": []\n\t      },\n\t      {\n\t        \"label\": \"Line #17\",\n\t        \"expanded\": false,\n\t        \"data\": {\n\t          \"location\": \"Building_1\",\n\t          \"equipment\": \"Line #17\"\n\t        },\n\t        \"items\": []\n\t      },\n\t      {\n\t        \"label\": \"Line #18\",\n\t        \"expanded\": false,\n\t        \"data\": {\n\t          \"location\": \"Building_1\",\n\t          \"equipment\": \"Line #18\"\n\t        },\n\t        \"items\": []\n\t      }\n\t   ]\n\t  },\n\t  \t  {\n\t    \"label\": \"Building_2\",\n\t    \"expanded\": false,\n\t    \"data\": \"\",\n\t    \"items\": [\n\t      {\n\t        \"label\": \"Line #23\",\n\t        \"expanded\": false,\n\t        \"data\": {\n\t          \"location\": \"Building_2\",\n\t          \"equipment\": \"Line #23\"\n\t        },\n\t        \"items\": []\n\t      },\n\t      {\n\t        \"label\": \"Line #24\",\n\t        \"expanded\": false,\n\t        \"data\": {\n\t          \"location\": \"Building_2\",\n\t          \"equipment\": \"Line #24\"\n\t        },\n\t        \"items\": []\n\t      },\n\t      {\n\t        \"label\": \"Line #25\",\n\t        \"expanded\": false,\n\t        \"data\": {\n\t          \"location\": \"Building_2\",\n\t          \"equipment\": \"Line #25\"\n\t        },\n\t        \"items\": []\n\t      },\n\t      {\n\t        \"label\": \"Line #27\",\n\t        \"expanded\": false,\n\t        \"data\": {\n\t          \"location\": \"Building_2\",\n\t          \"equipment\": \"Line #27\"\n\t        },\n\t        \"items\": []\n\t      },\n\t      {\n\t        \"label\": \"Line #28\",\n\t        \"expanded\": false,\n\t        \"data\": {\n\t          \"location\": \"Building_2\",\n\t          \"equipment\": \"Line #28\"\n\t        },\n\t        \"items\": []\n\t      }\n\t   ]\n\t  }\n\t]",
              "type": "script"
            }
          ]
        }
      }
    }
  }
]
Button
[
  {
    "type": "ia.input.button",
    "version": 0,
    "props": {
      "text": "Refresh"
    },
    "meta": {
      "name": "Button"
    },
    "position": {
      "x": 285,
      "y": 346,
      "height": 34,
      "width": 80
    },
    "custom": {},
    "events": {
      "dom": {
        "onClick": {
          "type": "script",
          "scope": "G",
          "config": {
            "script": "\tself.getSibling(\"Tree\").refreshBinding(\"props.items\")\n\tx = int(self.getSibling(\"Tree\").props.selection[0].split('/')[0])\n\tself.getSibling(\"Tree\").props.items[x].expanded = True"
          }
        }
      }
    }
  }
]

Thanks. I tested and it looks like a classic race condition. Try this refresh button, it works every time for me. Based on this I think it’s safe to say refreshBinding works asynchronously and sometimes your expand logic would fire too quick.

[
  {
    "type": "ia.input.button",
    "version": 0,
    "props": {
      "text": "Refresh"
    },
    "meta": {
      "name": "Button"
    },
    "position": {
      "x": 285,
      "y": 346,
      "height": 34,
      "width": 80
    },
    "custom": {},
    "events": {
      "dom": {
        "onClick": {
          "type": "script",
          "scope": "G",
          "config": {
            "script": "\tself.getSibling(\"Tree\").refreshBinding(\"props.items\")\n\tx = int(self.getSibling(\"Tree\").props.selection[0].split('/')[0])\n\timport time\n\ttime.sleep(.1)\n\tself.getSibling(\"Tree\").props.items[x].expanded = True"
          }
        }
      }
    }
  }
]

You could probably also avoid the need for a delay if you put the expand logic inside the binding (that is getting refreshed.
Since it seems like it will always look to the selection anyways.

items [
	  {
	    "label": "Building 1",
	    "expanded": false,
	    "data": "",
	    "items": [
	      {
	        "label": "Line #13",
	        "expanded": false,
	        "data": {
	          "location": "Building_1",
	          "equipment": "Line #13"
	        },
	        "items": []
	      },....
	     ]
	    if (self.props.selection[0]):
	       x = int(self.props.selection[0].split('/')[0])
	       items[x].expanded = True
	    return items
1 Like

I had thought it might be that.

I'm going to go with @victordcq and modify the binding and re-enable the expanded rung. It's a bit more elegant and will make it look like I know what I'm doing.

Excellent response times, guys. Thank you both.

1 Like

xD