Perspective getChild/Sibling

The problem

When building out screens that have interacting components, a common starting point from engineers that have used Vision in the past, is to use reference property paths or getChild/getSibling in scripting to reference other components.

Example

I.e. if you have a structure like this:
image

You might want to bind your label to your toggle switch, so you would do something like this:

or something that includes self.getSibling("ToggleSwitch").props.selected. Inherently, this is what the designer does for you when you use the property browser!

The outcome and "solution"

However as soon as you start nesting components into other containers, this will all fall apart because your reference is now wrong. This is why I always guide engineers to move "view-level state properties" (I made that phrase up) to the view custom properties.

This would now give you a custom property on your view called isSelected, then a bidirectional property binding on the props.selected for the toggle switch, and a property binding on the props.text for the label.

Now, whenever you move things around, all bindings still work because the view will never move, therefore the paths will always work.

This is my fundamental message in this post:

The real question...

This has been my default recommendation for years now, but the question is, what is wrong with the approach, and who has a different experience?

My Thoughts

My first and only thought as to why this solution might be "worse", is the performance implications of potentially doubling the necessary amount of bindings. However I don't know from an implementation standpoint how Perspective handles getting these properties. My guess is that using view custom properties causes the component to use a getView method to return the view, and then read the custom prop. Versus the relative reference needs to first getView and then getChild multiple times until it hits the base component, and then read the prop found there. Ultimately ending up in more "hops" to the component property than just having two bindings pointing at the view in the first place.

Hopefully this thread can be educative to anyone else coming back afterwards, to streamline their path to Perspective development success!

4 Likes

I do similar with Vision. I think back when I learned React in 2017 their whole methodology is the same in that data should only flow "down", ie children get their data from parent components, parent's usually shouldn't have to get data from their children components.

So I do similar with Vision insofar that usually I end up with many properties on the root container and my components referencing them directly or via some expression (like getting a single value from a dataset).

I do think this makes things much cleaner in general. The only time I break this is if the custom property is only used on the component directly then I will put it on the component to try to keep the root container from being cluttered, though the moment I find out that some other component does need that property, I will move it to the root container for reasons you've said (though I think any scripting paths will still break) but having one source of truth is reason enough imo.

3 Likes

Components, like anything else in Perspective, have insight into contexts/objects which are important to them. When it comes to components, this includes their parent and the view they reside in.

Basic implementation does include something akin to getView which returns a reference to the View of the component, where you would of course expect some property X to exist. Likewise, getSibling actually references the parent before then invoking getChild on the parent to find the specified "sibling".

So from a time-per-call perspective, referencing a component (or View node) takes...

  • view - 1 hop (self -> view)
  • child - 1 hop (self -> child)
  • sibling - 2 hops (self - > parent -> child-of-parent)

And then referencing any property takes one more step. you could make an argument for properties in objects in objects taking longer, but Im pretty sure we have deep reference optimization in place which minimizes that.

References to deeply-nested properties will always use child in some way (never sibling), and so you're looking at only 1 "hop" for each level.

# Let's pretend there's no optimization in place and say 6 hops here
# self -> parent -> child -> child -> child -> custom object -> property 
myvar = self.parent.getChild("LevelOne").getChild("LevelTwo").getChild("LevelThree").custom.isSelected

And so yes, referencing a property at the View level would take less hops.

# 3 hops
# self -> view -> custom object -> property
self.view.custom.isSelected

Now, as for "doubling" the number of bindings... Yes, it will, but unless you're putting thousands of binding in place on a View you shouldn't notice any impact.

6 Likes

In either case, I'd venture an educated guess that the "time for component traversal" is going to be utterly dwarfed by the "time to handshake between the session and backend", so it's all a bit moot.

In that event, the biggest things you're really optimizing for is development and maintenance ease. The side benefit of a few less component traversals is likely to be insignificant.

1 Like

What Iā€™m getting out of these comments is that at face value the practice is sound.

It would be nice if there was a way to put practice details like this in a cohesive place, so not only could new users learn how to interact with Ignition through the designer. But also how to build projects in quick and affective ways. This is definitely one of those problems I have watched countless engineers have to hash through the first time they refactor any part of a project.

2 Likes