Dynamic Flex Repeater with Instance Scripting: How do I set the Instance param values?

Hi all,
Been doing some reading on "Dynamic Flex Repeaters" and found a lot of good info on using a script transform on the props.instances.

Using this example by @YF129701, creating instances and setting fixed parameters are easy.

However, I would like to pass values that change depending on how many instances are needed, and values that would be associated with additional Flex Repeaters nested within them.

For my use case, there are three views: the parent, the repeaters within the parent, and the repeaters within the repeater.

Ideally, the primary and secondary repeaters would be using a script transform to set the number of instances needed, and passing other values as well.
Using this transform on a static value for testing:

	obj = {
		"instanceStyle":{"classes":""},
		"instancePosition": {}
	}
	objList = []
	for i in xrange(value):
		objList.append(obj)
	for o in objList:
		o["blockId"] = self.view.params.blockId
		o["zone"] = 1
	return objList

Returns this:


Well, each of those instances needs a different zone value. The values are not necessarily sequential either, which seems to make it more difficult. I could use a two column dataset of which zones to use, and this begs the question how would each scripted instance know which zone number to retrieve?
image

If this can be solved, I think I could use the same method to pass values to the secondary repeater to change the values you see with the light blue highlight. Currently, the secondary does not use a script transform, as I haven't figured out passing dynamic values through yet. Here are the current instances on the secondary:


bay and position determine the value of the light blue components.

Also, @bmusson mentioned he uses a message handler on the props.instances, in Ignition Early Access 2019. I am not sure this would be much different, maybe?

Thoughts on getting those dynamic values to pass to the parameters in each instance? Thanks!

Edit: I am also thinking of using the params on the secondary repeater as in/out, to pass changes back to the primary repeater, so that when the user saves the data, I can run fewer scripts on the primary repeaters, rather than 3x that on the secondary.

Don’t try to pass up state from inside doubly-nested repeaters, you’re gonna have a bad time.

Instead, pass everything down from the top level and have the inner levels send messages to the top level. The top level would perform the actions and then push the changes back down into the nested views.

Alternatively you can use @pturmel’s toolkit’s PageVarMap to hold your state; that would allow you to directly interact with the state from any level of the hierarchy.

4 Likes

What's your data source ? What's dynamic in your data, and what triggers refreshes ?

A parameter in the instances param is a dict. That's it. All you have to do is build a list of them.
If you have a nested structure, nest it in your params: You can pass a children parameter, which would itself be a list in the proper format to pass down to another flex repeater.
Another solution would be to build the children in the first repeater's instances, but that may or may not suitable to your data source.

Note that you do not have to pass the built-in parameters if you're not changing them: instanceStyle and instancePosition will receive default values if you don't pass them.

Give us an example of your input data so we can be more accurate in our answers.

  • The data comes from named queries.
  • The dynamic portions are the Zone, Bay and Position numbers/names.
  • The user will be inputting into components not shown which will be sent via messages as @bmusson mentioned.

So you are saying that I can build the instance object like this?

	obj = {
		"instanceStyle":{"classes":""},
		"instancePosition": {},
        "repeater01Params": {
            "zone": 1,
            "blockId": 100,
            "repeater02Params": {
                "bay": 1,
                "position": 1
                }
            }
	}

Data for display:
From the DB, something like this, pseudocode:

IF EXISTS ZoneNumber IN DB.ZoneTable
   AND IF EXISTS PositionNumber IN DB.PositionTable
   SELECT all relevant data

From the top view down:
View 1: gets a date, event ID, and Block ID. (This data comes from an onClick event on another View). These are passed to the primary Repeater via instances params.
View 2 (primary repeater) uses this data as params in a couple of named queries to pull the data for each secondary repeater. The thought here was to pass the data to the secondary repeater's params.

Here is an example of some of the data pulled into View 2 (primary repeater):

The secondary repeater (currently) does not have any queries associated, just bindings to the params.

Does this help/make sense?

I'll try to make things as simple as possible, to give you a starting point.
I made a table with the sample data:

select
	zone_id,
	zone_name,
	position_number,
	position_name
from test
order by zone_id, zone_name
zone_id zone_name position_number position_name
1 CZ01 1 01-01
1 CZ01 2 01-02
1 CZ01 3 01-03
2 CZ02 6 01-06
2 CZ02 7 01-07
2 CZ02 8 01-08

I made 3 views:

position:

takes 2 parameters, for position_number and position_name. 2 labels are bound to these parameters:
image

zone:

takes 3 parameters, for zone_id, zone_name, and positions. 2 labels (the grey ones) display zone id and name.
Then there's a flex repeater, bound to positions:


main:

contains the main flex repeater, bound to a named query, and with a script transform:

	from itertools import groupby
	from operator import itemgetter
	
	data = sorted(value, key=itemgetter('zone_id', 'zone_name'))
	return [
		{
			'zone_id': zone_id,
			'zone_name': zone_name,
			'positions': [
				{
					'position_number': row['position_number'],
					'position_name': row['position_name']
				} for row in group_data
			]
		} for (zone_id, zone_name), group_data in groupby(value, itemgetter('zone_id', 'zone_name'))
	]

NOTES:

  • The query binding uses json as its return format.
  • The code for the script transform should be in a library script, with imports outside of the function def.

That's the simplest version I could come up with.
A alternative solution would be to have 2 queries:

  • one that returns only the zone data,
  • and one that returns the position data for a zone
    Then you'd bind the first repeater to the zone query, and the second repeater to the position query, using the zone_id as parameter.
    This may or may not be better, depending on a bunch of parameters.
1 Like