Flex Repeater: Incrementing Tag Values on Button Click

Good Afternoon,

I’m working on dynamically updating a tag path within a Flex Repeater every time a new instance is created.

I have two views:

  1. Repeated View: Contains a label and an “Increase” button.
  2. Flex Repeater Test: Repeats the “Repeated View.”

Currently, the label in each repeated instance displays a tag associated with its index (e.g., “Row 1/Total” for the first instance, “Row 2/Total” for the second).

I'm aiming to modify the button’s behavior so that, when pressed, it increments the corresponding total in the tag path. For instance, clicking the “Increase” button in the second instance would increase the value associated with “Row 2/Total.”

I am currently having the following error:


Here are the current parameters and script for the “Increase” button in the Repeated View:

[Uploading: image.png…]()

	
	# Retrieve the dynamic tag path from the view parameters
	tag_path = self.view.params.tagPath
	print "tag_path result:"
	
	# Read the current tag value
	current_value = system.tag.readBlocking([tag_path])[0].value
	
	# Increment the tag value
	new_value = current_value + 1
	
	# Write the new value back to the tag
	system.tag.writeBlocking([tag_path], [new_value])
	
	# Optional debugging output
	system.perspective.print("Incremented {} to {}".format(tag_path, new_value))

This is the code within the "add row" event:

	# Get the Flex Repeater component
	repeater = self.getSibling("FlexRepeater")
	
	# Get the current list of rows (instances)
	current_rows = repeater.props.instances
	
	# Get the row number for the new row (after appending)
	row_number = len(current_rows) + 1  # Adding 1 because row_number is 1-based
	
	# Construct the dynamic tag path based on the row number
	tag_path = "[default]Flex Repeat test/Row {}/Total".format(row_number)
	print "Hello, Ignition!"
	
	# Read the current tag value
	current_tag_value = system.tag.readBlocking([tag_path])[0].value
	
	# Debugging: Print the constructed tag path and current tag value
	system.perspective.print("Tag Path: {}".format(tag_path))
	system.perspective.print("Current Value of {}: {}".format(tag_path, current_tag_value))
	
	# Define a new row with label, value, and tagPath set to the current tag path and value
	new_row = {
	    "label": str(current_tag_value),
	    "value": current_tag_value,
	    "tagPath": tag_path  # Adding tagPath to each instance
	}
	
	# Append the new row to the list
	current_rows.append(new_row)
	
	# Set the updated list back to the Flex Repeater
	repeater.props.instances = current_rows
	
	# Force UI update by reassigning the instances property
	repeater.props.instances = list(current_rows)
	
	# Debugging: Print the updated rows
	system.perspective.print("After Adding: {}".format(current_rows))

Any help would be great.

Thanks,

Your repeated view has tagpaths as a parameter, not tagpath.

Also, consider only passing the tag path to the repeated view and using that to make an indirect bidirectional binding to a custom property on the view. Then you can just increment that property's value by the desired amount and it will be pushed to the associated tag, no need to call tag.write* or tag.read*.

Thanks Ryan.

Could you explain alittle more? I don't quite understand

Sure,

Right now you are passing the repeated view the tag path and the tag's value(I assume) so you can display it.

Instead, create a custom property on the repeated view and indirectly bind to the tag using the tagPath parameter. Make sure the binding is bidirectional.

Bind your tag value display (label or whatnot) to the view.custom.tagValue property.

Then, the script on your increment button on the repeated view can be as simple as

def runAction(self, event):
	self.view.custom.tagValue += 1

Because you are using a bidirectional binding on the tagValue property, whenever it receives a new value from something other than the tag, it will write the new value back to the tag automatically.

This also lets your simplify your "add row" script a bit:

	# Get the Flex Repeater component
	repeater = self.getSibling("FlexRepeater")

	# Get the current list of rows (instances)
	current_rows = repeater.props.instances

	# Get the row number for the new row (after appending)
	row_number = len(current_rows) + 1  # Adding 1 because row_number is 1-based

	# Construct the dynamic tag path based on the row number
	tag_path = "[default]Flex Repeat test/Row {}/Total".format(row_number)
	print "Hello, Ignition!"
	
	# Debugging: Print the constructed tag path
	system.perspective.print("Tag Path: {}".format(tag_path))

	# Append the new row to the list
	current_rows.append({"tagPath": tag_path})

	# Set the updated list back to the Flex Repeater
	repeater.props.instances = list(current_rows)

	# Debugging: Print the updated rows
	system.perspective.print("After Adding: {}".format(current_rows))

Also, if you haven't already, consider browsing Inductive University. Its free, and explains the basics.

Thanks Ryan, yes I have been. It has been very helpful.

Got ya, so would you say that will be reading from the tag tree to the label or object in realtime? Not only when the instance is intially added?

Tag bindings (of any type) will update their value whenever the tag value changes, so yes "realtime", not just when the instance is added.

Right, becasue in my current setup with the code I provided in the begining, the tag tree value updates when button is pressed but not the value in the label.

Correct, because you essentially passed a snapshot of the tag value when you created the instance. Without any additional code or process to pass in the new value, it will stay the same as when you created the instance.

2 Likes

Got ya, so just so we are on the right track, I reviewed the Indirect binding video and its the following steps?

  1. in the repeated view create a custom property called "tagValue"

  2. Bind "tagValue" to the tag path in the tag tree, adding the "tagPath" prama to say which row to to look.

  3. Bind the tag value (custom.tagValue) to the labels text or view

So the result is I can see it will update in the view I wanted to be repeated but not in the flex repetor.

Have I missed something?

Thanks for you help

So in your image showing the binding setup, it looks like you are passing an instance number, but in the final picture you are passing the entire tag path. Look at the value of 'tagpath' in your instances property of the flex repeater.

With the indirect binding you have configured you are passing [default]Flex Repeat Test /Row [default]Flex Repeat Test/Row x /Total/Total instead of [default]Flex Repeat Test/Row x /Total hence the error binding to a tag.

Got ya. So in the tagValue bind, how do you do an indirect bind with tagpath {1} with {view.params.tagPath} without getting a config error?

Even if I assign values into tagPath or tag value I will still get an error.

If you are going to be passing the full tag path to the repeated view, then just have {tagPath} as the entire indirect bind path. Your flex repeater appears to be set up to pass the full tag path, so this will be the fastest.

It should be the same as

My example has an error because my tag path param is empty. Also, when building your example view, have an expected tag path in view.params.tagPath, it will make testing easier. In your case, have something like [default]Flex Repeater test/Row 1/Total

Also, I'm not sure if it matters but I typically use {1}, {2}, etc for indirect path references.

To be clear, your current issue stems from the fact that you are building the repeated view using an index value like 1 as the value for tag path when designing the view and testing the bindings, but when you are using the view in the flex repeater you are passing the full tag path.

Thats for all the help Ryan, I got it working. Would that approach would be similar for UDT's?

Correct, its the recommended approach for UDTs.

One additional piece of advice for UDTs is to pass only the path to the UDT, and use that path to indirectly bind the UDT members that you need to custom properties on the view. You can then toggle visibility of certain fields based on which properties were able to bind to tag.

This lets you use one view with mutliple UDTs that have some members in common. Think a motor control where the most basic motor has start/stop/status, but another might have start/stop/status/rpm/rpmSetpoint.