[Bug] Binding "moves" when duplicating properties

Hello,

I’m encountering a bug and didn’t know where to report it. It’s easy enough to work around, so I didn’t bother submitting a support ticket.

I have some parameters I’m passing to an embedded view (using perspective). These parameters include an array of objects:

One of these objects (object 4) has a binding on one of its values (position). If I duplicate an object above object 4, Ignition appropriately increments the number of the objects below where the duplicated object is, however the binding doesn’t shift down with the object. See the following screenshot after I duplicate object 2:

As you can see, object 2 is duplicated, the object that was previously object 3 is now object 4, and object 4 is now object 5. However the binding that was present on object 4 remained in place rather than shifting down with the object.

This is easy enough to work around by copying the binding, pasting it where desired and deleting the out-of-place binding.

Ignition Version: 8.0.5-rc1 (b2019100115)**

Regards,
Wade

I would believe this is expected behavior. Just as a binding in an object is associated with a specific key, a binding in an array is associated with a specific index. It’s not possible to create/move bindings dynamically, and the bindings on the component are not determined by the data IN the json, but the definition OF the json. This can be more easily understood by taking a look at the json file that determines how each view is built.

As an example lets take a scenario with a simple binding, on a menu tree’s props.items[0].target property. All it does is return “/Home”


And then a simple binding on the position.grow property, that returns 0

If we take a look at the view.json for that page, at the beginning of the json is a list of all the children in the root container like so:

"root": {
    "children": [
      {
        "meta": {
          "name": "MenuTree"
        },
        "position": {
          "basis": "523px"
        },
        "propConfig": {
          "position.grow": {
            "binding": {
              "config": {
                "expression": "0"
              },
              "type": "expr"
            }
          },
          "props.items[0].target": {
            "binding": {
              "config": {
                "expression": "\u0027/Home\u0027"
              },
              "type": "expr"
            }
          }
        },

If you notice on the menu tree item, under propConfig (the bindings), you have two dictionaries listed, one for position.grow and one for props.item[0].target. As you can tell here they are defined with specific locations in the overall JSON, but are unaware of the data that would end up being populated in that JSON. This is so you can know for certain where a binding will show up, no matter what the state of the page is.

I would say that in your case (If you potentially need this to be a bit more dynamic) you could create a binding that builds the entire props.params object dynamically, with the necessary logic for that position property defined in the script transform.

OR you could use a custom property that contains the same logic as your original binding, then add a script transform to iterate through that props.params.labelArray list and find the correct item and set the position there.

Hi Keith,

Thanks for the in-depth reply. Apologies if I didn’t make my post clear or if I’m misunderstanding your answer, but I’m not looking to dynamically create/move bindings during runtime. When I duplicated the item in the array, I was referring to right-clicking on the object in the property editor in designer and duplicating it there.

Your answer did make me go and have a look in the JSON and this is what I found. PS1_Geo_Button is the name of the embedded view that I have these parameters on.

Before duplicating the object in the array:

{
            "meta": {
              "name": "PS1_Geo_Button"
            },
            "position": {
              "height": 47,
              "width": 79,
              "x": 1216.796875,
              "y": 358.875
            },
            "propConfig": {
              "props.params.EngHigh": {
                "binding": {
                  "config": {
                    "fallbackDelay": 2.5,
                    "mode": "direct",
                    "tagPath": "[default]Pump Station 1/L01/L01/Process Value.EngHigh"
                  },
                  "type": "tag"
                }
              },
              "props.params.labelArray[4].position": {
                "binding": {
                  "config": {
                    "fallbackDelay": 2.5,
                    "mode": "direct",
                    "tagPath": "[default]Leachate Pond 1/Aerator/M04-LISP.value"
                  },
                  "type": "tag"
                }
              },
              "props.params.processValuePath": {
                "binding": {
                  "config": {
                    "fallbackDelay": 2.5,
                    "mode": "direct",
                    "tagPath": "[default]Pump Station 1/L01/L01/Process Value.path"
                  },
                  "type": "tag"
                }
              }
            },
            "props": {
              "params": {
                "fillColour": "#AC5F00",
                "labelArray": [
                  {
                    "in": false,
                    "label": "Inlet - xx m",
                    "position": 5,
                    "side": false
                  },
                  {
                    "in": false,
                    "label": "Top - 31.697 m",
                    "position": 100,
                    "side": true
                  },
                  {
                    "in": false,
                    "label": "Depth - 3.26 m",
                    "position": 100,
                    "side": false
                  },
                  {
                    "in": false,
                    "label": "Bottom - 0 m",
                    "position": 0,
                    "side": false
                  },
                  {
                    "in": true,
                    "label": "Aerator Inhibit SP",
                    "side": false
                  }
                ],
                "pitColour": "#AAAAAA",
                "title": "Pump Station 1"
              },
              "path": "Page/Embedded/Symbols/Geometric_Popup_Pit_Button"
            },
            "type": "ia.display.view"
          }

After duplicating the object in the array:

{
            "meta": {
              "name": "PS1_Geo_Button"
            },
            "position": {
              "height": 47,
              "width": 79,
              "x": 1216.796875,
              "y": 358.875
            },
            "propConfig": {
              "props.params.EngHigh": {
                "binding": {
                  "config": {
                    "fallbackDelay": 2.5,
                    "mode": "direct",
                    "tagPath": "[default]Pump Station 1/L01/L01/Process Value.EngHigh"
                  },
                  "type": "tag"
                }
              },
              "props.params.labelArray[4].position": {
                "binding": {
                  "config": {
                    "fallbackDelay": 2.5,
                    "mode": "direct",
                    "tagPath": "[default]Leachate Pond 1/Aerator/M04-LISP.value"
                  },
                  "type": "tag"
                }
              },
              "props.params.processValuePath": {
                "binding": {
                  "config": {
                    "fallbackDelay": 2.5,
                    "mode": "direct",
                    "tagPath": "[default]Pump Station 1/L01/L01/Process Value.path"
                  },
                  "type": "tag"
                }
              }
            },
            "props": {
              "params": {
                "fillColour": "#AC5F00",
                "labelArray": [
                  {
                    "in": false,
                    "label": "Inlet - xx m",
                    "position": 5,
                    "side": false
                  },
                  {
                    "in": false,
                    "label": "Top - 31.697 m",
                    "position": 100,
                    "side": true
                  },
                  {
                    "in": false,
                    "label": "Depth - 3.26 m",
                    "position": 100,
                    "side": false
                  },
                  {
                    "in": false,
                    "label": "Depth - 3.26 m",
                    "position": 100,
                    "side": false
                  },
                  {
                    "in": false,
                    "label": "Bottom - 0 m",
                    "side": false
                  },
                  {
                    "in": true,
                    "label": "Aerator Inhibit SP",
                    "position": null,
                    "side": false
                  }
                ],
                "pitColour": "#AAAAAA",
                "title": "Pump Station 1"
              },
              "path": "Page/Embedded/Symbols/Geometric_Popup_Pit_Button"
            },
            "type": "ia.display.view"
          }

I do notice that the bindings are stored separately inside “propConfig” rather than with the static properties in “props”. Is this what you meant by the bindings not being determined by the data IN the json (as the bindings are not stored in the labelArray json) but by the definition OF the json?

What I’m confused about is:

  1. Prior to my duplicating an object into labelArray, the “position” property in object 4 isn’t stored in “props” (As you’ve pointed out it has a binding on it so it’s stored in “propConfig”) - this is fine

  2. After duplicating an object the object (now object 5) now has a position property in “props”, so Ignition clearly recognises that this object has a position property even though it wasn’t listed in “props”. I imagine Ignition would have to see the property in “propConfig” to be aware of this. So then why doesn’t it update the property in “propConfig” to move the binding to the new array index?


Also, I’ve just noticed this bug (if it is a bug) only occurs when I duplicate an object in the array. If I delete an object, the binding moves correctly with the rest of the properties:

This screenshot was taken after:

  1. The binding was on “position” in array index 4
  2. I duplicated the object in index 2. Objects 3 and 4 incremented their index. The binding remained at array index 4
  3. I deleted the duplicated object in index 3. Objects 4 and 5 decremented their index. The binding moved from array index 4 to array index 3

Interesting I didn’t know this Is how the delete behavior worked, that might lead me to believe that the “duplicate” action should move it as well, I guess I’m not sure on that one.

Bumping this thread to see if we can get an official answer. Is this behavior a bug or should bindings move when deleting/inserting elements in an array?

I just found this out the hard way after deleting an array element in a flex repeater. Now all my bindings are messed up and I have to basically delete the entire thing and start over.

My flex repeater contained different types of views, so the structure of the parameters didn’t line up with the bindings when I deleted an element. Now some objects show they have a binding somewhere (highlighted in blue), but the parameter doesn’t exist, so I can’t get to the binding to remove it.

It would be nice if bindings moved with the elements of the array. Not sure if this is possible though.

Side note: It would be nice to have a “copy all child bindings” in the right click menu. It would make copying/pasting bindings in large objects much faster.

Looking for an answer to this as well. Seems like a bug to me. I should add that this also occurs when moving properties.

There’s another thread on this topic somewhere. Not handy, sorry. Bottom line: it is expected and fundamental behavior of bindings where an array is involved. The binding targets a specific subscript in the array. Move elements around and the binding is now pointing at the “wrong” element.

Don’t expect a fix any time soon, if ever.

If that is the case, why does it work correctly when deleting a property?

Before - Binding is on 4


After deletion - binding is on 3

Oh, you’ve change the problem description to designer editing behavior rather than runtime array changes. I agree that the designer should get it right–it knows what you are doing and should be able to adjust. At runtime, bindings are already “locked in”, so to speak.