Accessing document custom tag property members the same way as document tag members

No, they function quite a bit differently.

I'm getting confused about your data delivery issue (for which I recommend pageVarMap()) and your indirection/coalescing issue (where I remain adamant that you not use tag()).

Please share the full coalesce/tag expression you are trying to refactor.

1 Like

I didn’t mean replace literally - of course I’d have to refactor a bit. Was curious about the performance considerations assuming I could somehow successfully use tags(singleTagPath) instead of tag(tagPath)

Won’t be today, have to check out previous commit, don’t plan to open the laptop until late.

Primary description of the differences:

For a single tag, an indirect binding will be better than my tags() function.

Edit: See new beta that may be useful to you:

2 Likes

seems {some.parameter[0]} retains quality (as opposed to {some.parameter}[0]?… ahhhh but it parses {value[0]} as a tagPath instead of a property
but the former is kinda useless in this case because...

this is blowing my mind:

  • I can nest QVs anywhere I want with a script transform
  • I can nest QVs only in objects/mappings/dicts in an expression structure binding - but not inside lists/arrays??
  • of course I can manually nest QVs anywhere in a complex object by binding individual child values
    • but If I bind another parameter to it (the parent object), then all the nested QVs are retained (inlcuding inside lists/arrays)!?
  • i’m guessing the behavior you describe is somewhere in the mix here, but not everywhere? Perhaps because when it works it’s referencing an existing object, as opposed to being constructed? But that alone doesn’t seem to explain it.

this completely solved my issue(s)!!! - that qvAt() is :fire:!

but I’m scared it’s gonna be a breaking change for others?

that being said, i’d honestly avocate doing the same with asList/Map whatever other functions move nested qualities to the parent

small suggestion that might make this even better until IA gives the root issue some consideration:

retain the tag quality at each top-level array object like this transform does for the first (because it’s the only one i care about)
{
  "type": "expr",
  "config": {
    "expression": "qvAt(\r\n\twhere(\r\n\t\ttags({this.custom.paths}),\r\n\t\tqualityOf(qvAt(it(), 1)) !\u003d \"Bad_NotFound\"\r\n\t),\r\n\t0\r\n)"
  },
  "transforms": [
    {
      "expression": "qualifiedValue({value}, split(qualityOf(qvAt({value}, 1)),\u0027_\u0027)[0], qualityOf(qvAt({value}, 1)))",
      "type": "expression"
    }
  ]
}
which would allow a 'downstream' binding like this to retain quality without a separate indirect tag binding to the tagPath
{
  "type": "property",
  "config": {
    "path": "this.custom.tagData.value"
  },
  "transforms": [
    {
      "expression": "{value}[1]",
      "type": "expression"
    }
  ]
}

I think otherwise to get both tagPath and tag value with quality (based on said tagPath), I either need two nearly identical tags() functions or one tags() function and an indirect tag binding.

if that happens, unsure the quality would even still be necessary or useful/accessible directly on the tag value - unless you turned it into a map instead of a list?…

oooh this (ugly) binding seems to put the quality at the top level presumably because of asMap’s current implementation...
{
  "type": "expr",
  "config": {
    "expression": "asMap(\r\n\t\u0027path\u0027,\r\n\tqvAt(\r\n\t\tqvAt(\r\n\t\t\twhere(\r\n\t\t\t\ttags({this.custom.paths}),\r\n\t\t\t\tqualityOf(qvAt(it(), 1)) !\u003d \"Bad_NotFound\"\r\n\t\t\t),\r\n\t\t\t0\r\n\t\t),\r\n\t\t0\r\n\t),\r\n\t\u0027value\u0027,\r\n\tqvAt(\r\n\t\tqvAt(\r\n\t\t\twhere(\r\n\t\t\t\ttags({this.custom.paths}),\r\n\t\t\t\tqualityOf(qvAt(it(), 1)) !\u003d \"Bad_NotFound\"\r\n\t\t\t),\r\n\t\t\t0\r\n\t\t),\r\n\t\t1\r\n\t)\r\n)"
  },
  "transforms": [
    {
      "expression": "{value}\r\n//qualifiedValue({value}, split(qualityOf(qvAt({value}, 1)),\u0027_\u0027)[0], qualityOf(qvAt({value}, 1)))",
      "type": "expression"
    }
  ]
}

but I have a hunch based on my poking around above that if you modify asMap...

and perspective might not discard them? maybe just a tagsAsQvMap() alternative?

Thank you for your incredible help by the way!

No, qvAt() has never existed, so nothing to break. The only real change was for tags() to return good for the content even if some content is bad. That is what make it iterable. (My looping functions bail out if the source list is bad quality.)

For practical purposes, you will need to act upon qvAt() for tag quality in a loop immediately wrapping tags(), like the example with where().

just to be clear, this is exactly what I was referring to - i could see a scenario where one would expect the existing behavior over new. I’m fully aware qvAt() is new

this is also exactly what I’ve done, but extended by copying the nested [1] quality up to the top item level, which indeed allows for something closely resembling my original need - direct access to nested values via bracket operator while retaining quality. Ignition retains the quality of a parameter binding to the top-level, and then bracket operator in an expression transform returns the value. It’s not that ugly IMO and a small change to the new tags() would allow for the same thing, without the need to copy up the qualities.

also it seems a asMap version of tags might mitigate the nested quality retention issues altogether

penny for your thoughts (if you have time)?

also, here’s that `tag()` binding transform I was trying to refactor
{
  "type": "expr",
  "config": {
    "expression": "coalesce(\r\n\t{view.params.instanceData.fullPath},\r\n\t{view.params.rowData.fullPath} + \u0027/\u0027 + {view.params.instanceData.subPath} + \"/\" + {view.params.column}\r\n)"
  },
  "transforms": [
    {
      "expression": "if(\r\n\tqualityOf(tag({value})) \u003d \u0027Bad_NotFound\u0027 \u0026\u0026 !isNull({view.params.altColumns[0]}),\r\n\t{view.params.rowData.fullPath} + \u0027/\u0027 + {view.params.instanceData.subPath} + \"/\" + {view.params.altColumns[0]},\r\n\t{value}\r\n)",
      "type": "expression"
    }
  ]
}
and another binding referencing that one
{
  "type": "expr",
  "config": {
    "expression": "coalesce(\r\n\t{view.params.instanceData.formatString},\r\n\ttag({this.custom.fullPath} + \".formatString\")\r\n)"
  }
}

went from 5 parameters to 10+, but made it a bit more flexible (with the help of toolkit)

regardless, Thanks again!

That should be split with two additional custom properties:

  • String of the top expression, with no transform.
  • An indirect tag binding that uses simply {1} as the path and the {1} reference just points at the above string property.

Then, the final binding is just an expression binding, no transform, that is the original expression transform where tag({value}) is replaced by {view.custom.whatever} holding the live tag value.

The string and indirect tag binding could be collapsed into one property with the indirect expression binding, but I find that less intuitive.

The next binding similarly needs another indirect tag binding.

I'll look at having asMap() and asList() return good when passing through nested bad quality.

Meanwhile, consider using groupBy() to separate distinct tags from properties of those tags when feeding a big list of tags with properties to a single call to tags().

1 Like