Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

actionVariableKey not working properly for nested arrays #2467

Open
SimonB27P opened this issue Mar 12, 2024 · 3 comments
Open

actionVariableKey not working properly for nested arrays #2467

SimonB27P opened this issue Mar 12, 2024 · 3 comments
Labels
bug Something isn't working main ui Main UI

Comments

@SimonB27P
Copy link

The problem

actionVariableKey in oh-components is not working properly. In certain circumstances, when an array item has been inserted as item 1 (and as a result item 0 is null), item 0 cannot be changed. This seems to crash the action completely because if clearVariable is also set, the variable is not cleared.

This only happens in a nested array

Expected behavior

ActionVariableKey should be able to specify any object key or array position. If array item x is set by the action, then array items 0, 1... x-1 should be set to null. If array item 0 is then changed by a later action that array item should change without affecting the rest of the array. This works successfully for a simple array.

However, consider
actionVariable: testVar
actionVariableKey: ="[x].[y]"

If x is 0, the first item in the array is set and testVar has the value [["Item 0"]]. The second item in the level 1 array is as yet undefined. Perhaps we add item 2 of the second item using the actionVariableKey "[1].[1]" and we get [["Item 0.0"],[null,"Item 1.1"]]. At this point any of the array items can be changed at will.

However, if the second item in the parent array is set first, and is itself an array, the array becomes [null,["Item 1.0"]]. The first item is set to be null. From this point it is impossible to change the value of the first item in the parent array.

Steps to reproduce

The following sample widget exhibits the described behaviours

uid: arrayTest
tags: []
props:
  parameters: []
  parameterGroups: []
timestamp: Mar 12, 2024, 1:38:03 PM
component: f7-card
config: {}
slots:
  default:
    - component: oh-button
      config:
        text: ="set item 1 - item 0 is null"
        action: variable
        actionVariable: testVar
        actionVariableKey: ="[1]"
        actionVariableValue: ="Item 1"
    - component: oh-button
      config:
        text: "set item 0 - null changed to new value"
        action: variable
        actionVariable: testVar
        actionVariableKey: ="[0]"
        actionVariableValue: ="Item 0"
    - component: oh-button
      config:
        text: Clear
        action: variable
        clearVariable: testVar
    - component: f7-block
      config:
        style:
          height: 25px
      slots:
        default:
          - component: Label
            config:
              text: =JSON.stringify(vars.testVar)
    - component: oh-button
      config:
        text: "add item 0.0 first - item 0 sucesssfully added"
        action: variable
        actionVariable: testVar1
        actionVariableKey: ="[0].[0]"
        actionVariableValue: ="Item 0.0"
    - component: oh-button
      config:
        text: ="set item 1.1 - again successfully added, item 1.0 is null"
        action: variable
        actionVariable: testVar1
        actionVariableKey: ="[1].[1]"
        actionVariableValue: ="Item 1.1"
    - component: oh-button
      config:
        text: "set item 1.0 - item 1.0 changed from null to new value"
        action: variable
        actionVariable: testVar1
        actionVariableKey: ="[1].[0]"
        actionVariableValue: ="Item 1.0"
    - component: oh-button
      config:
        text: Clear
        action: variable
        clearVariable: testVar1
    - component: f7-block
      config:
        style:
          height: 50px
      slots:
        default:
          - component: Label
            config:
              text: =JSON.stringify(vars.testVar1)
          - component: Label
            config:
              style:
                text-align: center
              text: Now try to reverse the order
    - component: oh-button
      config:
        text: ="set item 1.1 - OK - unset items added as null"
        action: variable
        actionVariable: testVar2
        actionVariableKey: ="[1].[1]"
        actionVariableValue: ="Item 1.1"
    - component: oh-button
      config:
        text: "set item 1.0 - works"
        action: variable
        actionVariable: testVar2
        actionVariableKey: ="[1].[0]"
        actionVariableValue: ="Item 1.0"
    - component: oh-button
      config:
        text: "Now try to set item 0.1 - item 0 of parent array fails to change"
        action: variable
        actionVariable: testVar2
        actionVariableKey: ="[0].[0]"
        actionVariableValue: ="Item 0.0"
    - component: oh-button
      config:
        text: Clear
        action: variable
        clearVariable: testVar2
    - component: f7-block
      config:
        style:
          height: 25px
      slots:
        default:
          - component: Label
            config:
              text: =JSON.stringify(vars.testVar2)

Your environment

runtimeInfo:
  version: 4.1.1
  buildString: Release Build
locale: en-GB
systemInfo:
  configFolder: /etc/openhab
  userdataFolder: /var/lib/openhab
  logFolder: /var/log/openhab
  javaVersion: 17.0.9
  javaVendor: Raspbian
  osName: Linux
  osVersion: 6.1.21-v8+
  osArchitecture: arm
  availableProcessors: 4
  freeMemory: 160820752
  totalMemory: 391938048
  uptime: 3290202
  startLevel: 100
addons:
  - automation-jsscripting
  - binding-enocean
  - binding-homematic
  - binding-hue
  - binding-samsungtv
  - binding-squeezebox
  - binding-zwave
  - misc-hueemulation
  - misc-openhabcloud
  - persistence-rrd4j
  - transformation-map
  - ui-basic
clientInfo:
  device:
    ios: false
    android: false
    androidChrome: false
    desktop: true
    iphone: false
    ipod: false
    ipad: false
    edge: false
    ie: false
    firefox: true
    macos: false
    windows: true
    cordova: false
    phonegap: false
    electron: false
    nwjs: false
    webView: false
    webview: false
    standalone: false
    os: windows
    pixelRatio: 0.8955223880597015
    prefersColorScheme: dark
  isSecureContext: true
  locationbarVisible: true
  menubarVisible: true
  navigator:
    cookieEnabled: true
    deviceMemory: N/A
    hardwareConcurrency: 4
    language: en-GB
    languages:
      - en-GB
      - en
    onLine: true
    platform: Win32
  screen:
    width: 2144
    height: 1206
    colorDepth: 24
  support:
    touch: false
    pointerEvents: true
    observer: true
    passiveListener: true
    gestures: false
    intersectionObserver: true
  themeOptions:
    dark: dark
    filled: true
    pageTransitionAnimation: default
    bars: light
    homeNavbar: default
    homeBackground: default
    expandableCardAnimation: default
  userAgent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:123.0) Gecko/20100101 Firefox/123.0
timestamp: 2024-03-12T13:38:29.201Z
@SimonB27P SimonB27P added bug Something isn't working main ui Main UI labels Mar 12, 2024
@SimonB27P
Copy link
Author

As an addendum, I was playing around with workaround solutions until this is fixed.

The issue is also there if the variable is created in one operation. For example:

If actionVariableKey is left unused but actionVariable is set to ["anything",["Item 1.0","Item 1.1"]] then the item "anything" cannot be changed by a subsequent variable action.

actionVariable is a very powerful solution to give much more flexibility and control in what the UI does, most importantly allowing the user to make various changes before committing these to actual live Items. This bug is making it seriously difficult to achieve what I want in my custom widgets and I am having to write a lot of unnecessary, slow and cumbersome code to try to get around it.

@JustinGeorgi
Copy link
Contributor

I wouldn't really consider nested arrays as widget variables a currently supported feature. The actionVariableKey simply wasn't designed to handle nested arrays, and the fact that what you've got up to this point works at all is more a quirk of javascript object definition than anything else.

The problem you are encountering is because when you create an array by specifying a element that is beyond the zero index:

myNewArray[1] = 'Some value`

javascript has no guidance on what value to use for any non-specified elements up to that defined element and so substitutes null as you've seen.

myNewArray = [null,'Some value']

However, null is not itself an array and so you cannot access or assign any additional information to it using array nomenclature: null[0] is nonsense as far as javascript is concerned.
To see this in action, add the following button between your 2nd and 3rd buttons of your last series:

    - component: oh-button
      config:
        text: "Now initialize element 0 of testVar2 to an array"
        action: variable
        actionVariable: testVar2
        actionVariableKey: ="0"
        actionVariableValue: =[]

Once you do that and the first element of your test array is now an empty array instead of null you will find that you can assign other elements inside that empty array.

There is actually a possibility that my recent PR #2533 will address this issue by allowing you to define the nested array variables in advance, but again that's not so much officially supported as a fortunate side-effect.

The other way to address this (if your nested array is a fixed size) would be to not use actionVariableKey at all and just set the variable to the full nested array each time using only changing the one element you want adjusted by that button. For example:

        actionVariable: testVar2
        actionVariableValue:
          - =((vars.testVar2 && vars.testVar2[0]) === undefined)?([]):(vars.testVar2[0])
          - - =vars.testVar2[1][0]
            - 'Item 1.1'

@SimonB27P
Copy link
Author

Hi Justin

I think that is very much the conclusion I came to. I can see that it is probably down to the way Javascript handles arrays under the hood but I thought I might be able to change that first element. As you'll see from my second post, it's not just a problem with the null value. If the first element is anything, for example a string, it still can't be changed if the second item is created as a nested array.

I do have a workaround. Given my use-case means doing this inside repeaters, I have the complete array available anyway so, as you suggest, I have discarded the actionVariableKey and, instead, every change I make recreates the entire array from scratch as a single actionVariable. I can toggle items by toggling the state of the array item that matches the loop index. Everything else just gets put back in the same state. Other controls that need to know which items are selected or not again act on the relevant array item.

It requires a lot more code in the widget - arrow functions with map() methods and more - but it works well. I'll post it to the marketplace shortly. I have to say, as I learn, I'm finding that the new UI is very powerful. There are a few workarounds needed like variable defaults as discussed but your PR would help with that enormously - I'll look forward to that becoming part of Openhab.

I'm finding using array variables in the UI very powerful though because I can then send a lot of UI input data back to my rules in one go or, alternatively, the user can cancel the operation without having actually changed any live item states.

Thanks for your interest in this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working main ui Main UI
Projects
None yet
Development

No branches or pull requests

2 participants