How to reliably call a function after `refreshBinding`

Hi, I'm trying to wrangle the Perspective Tree component to do what I want it to, but I'm having the darndest time, so here I am back on the Forums, complaining.

Basically, what I'm trying to do is to pre-select a node in the Tree onStartup (view). What is happening that is preventing me from accomplishing this successfully is that I do a call to refresh the items prop of the Tree right before I get the item to select.

Something like

...
self.path.to.tree.refreshBinding('props.items')
items = self.props.items[0].items
...

I get an error on the second line which says Error: NoneType object is unsubscriptable - how I interpret this is that this happens because the props.items are queried from a database, so there is a slight delay in the refresh of that property, but the method call to refresh binding seems to happen asynchronously, so that the second line (items = self.props.items) fails because the props.items property is still null.

Just for completeness, I added a "Debug" button on my view that essentially does the exact same thing and I get the behavior I want. When I do do that, my best guess is that props.items is already populated, so I can execute the script on a button however many times I want, even though I'm refreshing the binding, just like I am on the onStartup view script.

What I would like to do is to only call the items = self.props.items[0].items line after the binding has been refreshed and the property is populated - is there a good way to do this?

Just to follow up on this, I added a time.sleep(2) call between the refreshBinding call and the items assignment and this fixed my issue, so I'm confident this is happening because the props.items property is still null when the assignment happens.

How do I fix this in a good way? I refuse to throw a change script on the props.items property that will select an item for me when the previousValue was null - I'm moving away from screen design using this paradigm heavily because it is absolute hell trying to troubleshoot / debug things [unless, of course, I can be persuaded otherwise]. IMHO, property change scripts should be used very intentionally and sparingly.

Well, that is the point that gets the notice when the item objects arrive. The change script is the right way to execute more script actions after the refresh completes.

Make it a one-liner that delegates to a project library script to aid troubleshooting. (Do this everywhere, really.)

I spoke with my friend who seems to always have the best answers when I'm stuck. What I ended up doing to avoid the change script was to take the runScript binding I had on the Tree props.items and ran that within my script, then did assignment to the Tree's props.items, then I was able to get the items variable to work as it should.

So, I went from

self.refreshBinding('props.data')    # binding is a runScript expression to my.shared.script(p1,p2)
items = self.props.items[0].items
...

to

newTreeItems = my.shared.script(p1, p2)     # generates tree instances
self.props.items = newTreeItems

items = self.props.items[0].items
...

This has worked reliably ever since I implemented it, so this is what I'll keep.

Watch out! Even the assignment to self.props.items is asynchronous. You should use newTreeItems[0].items instead of referencing self.prop.items[0].items.

1 Like

That is good to know that the assignments are async... I realized I could just use newTreeItems as I was watching you type, waiting to see if I was about to get OWNED with FACTS AND LOGIC.

Thanks for your input!

Edit just for completeness:

As Phil noted, I could just use the newTreeItems property. However, I did have to change from dot notation to [""] to access object properties. So, my script ended up being like

newTreeItems = my.shared.script(p1, p2)     # generates tree instances
self.props.items = newTreeItems

items = newTreeItems[0]["items"]    # instead of [0].items
...
1 Like