refreshBinding not working on flex repeater props.instances

I have a flex repeater with a query binding on its props.instances property, and I’m trying to refresh it on button click, but it doesn’t appear to do anything when I run:

self.getSibling('FlexRepeater').refreshBinding('props.instances')

Should that work?

2 Likes

That looks correct. Any insight into the binding? Anything in the Gateway’s Status logs?

Any updates on this? I’m trying to refresh a Flex Repeater from a button click on a button in the view template inside the repeater. Is that possible and if so can anyone give a hint on the navigation needed - I tried self.getParent() on the button in the view template - but that gets the root of that view - I need to get from there to the Flex Repeater which has multple instances of that view - so I though maybe .getParent() again since I would consider the Flex Repeater the parent of the views it contains, but that didn’t work. I wouldn’t think a sibling would work, I would think that might get me other instances of the view within the repeater.

Any tips appreciated.

1 Like

The button would have to use system.perspective.sendMessage() to broadcast the refresh request (on the page or session scope), and the repeater would have to use a message handler to listen for that request on the page or session scope. Embedding components are like a frame to hold a view, but the internal View has no idea it's even being put into a frame and so has no concept of a "parent"; therefore, you cannot reference anything "above" the instanced/templated/internal view from within it.

Does the button click on the view in the repeater also trigger an onClick event on the repeater itself? Or is the click event only on the component clicked and not on any of the containers it’s in? I was thinking to try to just put the script up at that level - extra refreshes wouldn’t really hurt anything if the user clicked on something other than the button in the sub-view.

Yes, the Flex Repeater will indeed register a click event.

Using the click event didn’t work because the DB write is happening on the same click and looks like that hasn’t been processed in time for the refreshBindings to see it - so if I click twice I see the information refresh, but not once.

I think for now I’ll just put a refresh button on the page so users knows a manual refresh is needed, and figure out the sendMessage technique later when I have a bit more time.

Thanks for the assistance.

Yah, if the instanced View is performing logic the repeater’s event is likely to occur before the logic in the instanced view has completed. That’s why I originally recommended you place the refresh call there, because you can define when the message is sent.

I am doing something similar to the first poster. I have a flex repeater I want to refresh the bindings when I click a button somewhere else in the view. The button is not part of the repeater.

self.parent.parent.getChild("SetpointsRepeater").refreshBinding('props.instances')

When I click the button I modify a value I can see a value change elsewhere that is tied to the repeater, so when that changes the repeater bindings need to update to reflect the change. I am having no luck making this happen.

I just verified that it is working as expected. Please verify that you are not encountering an issue with your script by clicking your button in a running session (or Designer in preview mode) and the checking your Gateway logs for any reported script errors. You could also place a line of logging at the end of your script to verify completion, or you could place an onChange script on the instances property of your Flex Repeater.

If there is a naming (or pathing) issue then you will see something like this:

AttributeError: 'NoneType' object has no attribute 'refreshBinding'

if something else in your script is failing, you would see a different failure reported.

I put the script in a button and run it in the designer in Run Mode and I get no error at all. but it really doesn’t seem to be running the refresh binding on that repeater. I am a few minor revisions back on perspective maybe I need to update to the latest version. Should the refresh binding command work in the designer mode? I added logging in my button script so that after it calls the refresh binding on my repeater it logs “Button Clicked to Refresh Repeater”. That value logs and I get no errors in my script. I Then put a change script on the props.instances of the repeater to say the binding has been refreshed, and that never logs. So its as if the refreshBinding never gets executed.

My testing was done in the Designer, so it definitely works there. I double-checked path traversal and everything seems in order. I can’t think of any changes we made in this area, so I suspect an update wouldn’t change anything.

How positive are you that the data from the query should result in new/different data being returned for the binding? The change script would not log anything if the binding was refreshed but the data had not changed.

Its definitely modifying the dataset. When I do it in designer if I click the button which modifies the data set (And tries to refresh the binding) then I click on the binding on the repeater instances and click ok (Not modifying anything on the binding) the datset changes. So it can see the change when I do that.

For the repeater instances I bring in a tag binding to a dataset and then I use a transform to add 2 more columns to the dataset. So the main tag binding isn’t changing but the data being appended in the transform is. Would this be causing problems?

The picture shows the original dataset then I add two rows for visibility conditions on if I want to display the alarm or Shutdown setpoints. That's what is done in the scripting below.

Version: 8.1.3

image

def transform(self, value, quality, timestamp):
Transform the incoming value and return a result.

Arguments:
	self: A reference to the component this binding is configured on.
	value: The incoming value from the binding or the previous transform.
	quality: The quality code of the incoming value.
	timestamp: The timestamp of the incoming value as a java.util.Date

# convert the incoming value data
pyData = system.dataset.toPyDataSet(value)

#Set Variables for visibility Columns
colCount = pyData.getColumnCount()
columnNameAlm="showAlms"
columnNameSD="showSDs"
columnAlmData=[]
columnSDData=[]

#Build Visibility Columns
for i in range(pyData.getRowCount()):
	columnAlmData.append(bool(self.view.custom.showAlms))
	columnSDData.append(bool(self.view.custom.showSDs))

pyData2= system.dataset.toPyDataSet(system.dataset.addColumn(pyData, colCount, columnAlmData, columnNameAlm,bool))
pyData2= system.dataset.toPyDataSet(system.dataset.addColumn(pyData2, colCount+1, columnSDData, columnNameSD,bool))

# get the header names
header = pyData2.getColumnNames()
# create a blank list so we can append later
newList = []
 
# step through the rows
for row in pyData2:
	# create a new blank dictionary for each row of the data
	newDict = {}
	# use an index to step through each column of the data
	for i in range(len(row)):
		# set name/value pairs
		newDict[ header[i] ] = row[i]
	 
	# append the dictionary to list
	newList.append(newDict)
	 
# return the results
return newList

Ah, okay, I suspect something else is going on here. I’d been operating under the assumption that you were using a Named Query to populate your instances, and so all of my replication had been with Named Queries. Tag bindings (even indirect) shouldn’t need to be updated because that binding is “live” or always listening for changes.

I verified this by manipulating my own Dataset tag and I watched my Flex Repeater update instances and/or add additional instances as my data was committed.

So there are still a few areas it could be:

  1. Perspective property names may never have spaces, so make sure your dataset column headers (and therefore the object keys) contain no spaces.
  2. Place a tag change script and log to the Gateway whenever this dataset tag changes. Now click your button and see if the button script logs its last line before the tag registers a value change. You might be encountering a race condition where you asynchronously update the tag, but are synchronously refreshing the binding, therefore refreshing before the value has changed (unlikely, but I’ve seen weirder things).
  3. Perspective also doesn’t like numbers as property keys; make sure none of your property keys are numbers.

Could you expand one of your instance entries so that we could see what is being supplied to an instance?

So the dataset tag linked through the indirect tag binding isn’t what’s changing, that tag actually never changes so maybe that’s why the tag change script never executes. The dataset change happens in the transform section of the binding. When it tries to determine which columns to show and hide and appends new columns to the static dataset.

image

I would certainly hope that's true.

My understanding is that the transform will only run in the event that the binding updates, which in this case would only occur if the tag changes.

Instead of using a binding on the tag directly, consider using a property which you can then use a project script or otherwise to modify.

Correct. If you refresh the binding, the binding will check against the value returned from the original datasource (your tag). If that value has changed, then the binding re-evaluates. This is why I had been assuming a Named Query, because a tag binding of any sort will automatically re-evaluate whenever the tag data changes.

1 Like

ok, well then I will use something else on the binding that changes when I need it to and then add all the logic under the transform section to build the dataset.

Look into using the Expression Structure binding type. It will allow you to set one value for your tag, a second for the showAlms value, and a third for the showSDs value. Then, if any of those three values change the entire binding re-evaluates.