Problem referencing an item in my custom widget

I have it defined in the props section (the item). This item contains a list of options that I want to display in a list of options when I press.
If I put the item directly, the list is filled in and the options appear. However, when I try to do it using props, I don’t succeed.

Definition:

props:
  parameters:
    - context: item
      description: Plant for the query
      label: Plants
      name: item_plants
      required: true
      type: TEXT

Place for the call:

- component: f7-row
      config: {}
      slots:
        default:
          - component: oh-repeater
            config:
              sourceType: array
              for: selection
              in:
                - title: Planta
                  icon: f7:placemark
                  item: =props.item_plants
              fragment: true

Thanks greetings.

1 Like

I have moved this question over to the category for questions. The Add-on Marketplace category is reserved for publishing complete widgets, rules, or bindings that other users can download.

To your question: This is a known limitation. You cannot use expressions in the elements of a YAML array (the - followed by title:, icon:, and item:). Without knowing why you need this to be a dynamic property it is difficult to advise you on what the best workaround might be. For now, I would suggest just setting item to the text item_plants:

 - title: Planta
   icon: f7:placemark
   item: item_plants

Then anywhere in the following widgets you can reference the item name by calling it with the property object:

props[loop.selection.item]

I need it to be a dynamic property to replicate the widget with different plants without having to modify the code and do it through the props.

Pd: thanks for placing my post in the right place.

I attach all my code:

uid: widget_27f9a28a48
tags: []
props:
  parameters:
    - description: Title for the query widget
      label: Title
      name: widget_title
      required: true
      type: TEXT
    - context: item
      description: Plant for the query
      label: Plants
      name: item_plants
      required: true
      type: TEXT
    - context: item
      description: Measure for the query
      label: Measures
      name: item_measures
      required: true
      type: TEXT
  parameterGroups: []
timestamp: Dec 3, 2021, 4:41:00 PM
component: f7-card
config:
  class:
    - padding
  outline: true
slots:
  default:
    - component: Label
      config:
        text: =(props.widget_title)
        style:
          font-size: 16px
          font-weight: bold
    - component: f7-col
      config: {}
      slots:
        default:
          - component: oh-input-card
            config:
              clearButton: true
              sendButton: true
              required: false
              validate: false
              validate-on-blur: false
              title: Desde
              type: datetime-local
              name: from_date
              item: test_date_string
              footer: =items.["test_date_string"].state
    - component: f7-col
      config: {}
      slots:
        default:
          - component: oh-input-card
            config:
              clearButton: true
              sendButton: true
              required: true
              validate: true
              validate-on-blur: true
              title: Hasta
              type: datetime-local
              name: to_date
              variable: var_to_date
              item: test_date_string
              footer: =items.test_date_string.state
    - component: f7-row
      config: {}
      slots:
        default:
          - component: oh-repeater
            config:
              sourceType: array
              for: selection
              in:
                - title: Planta
                  icon: f7:placemark
                  item: item_plants
                - title: Medida
                  icon: f7:tag
                  item: item_measures
              fragment: true
            slots:
              default:
                - component: f7-col
                  slots:
                    default:
                      - component: oh-list
                        config:
                          id: list
                        slots:
                          default:
                            - component: oh-list-item
                              config:
                                title: =loop.selection.title
                                after: =items[loop.selection.item].displayState
                                smartSelect: true
                                icon: =loop.selection.icon
                                iconColor: default
                                popupOpen: =".selectionPopup_"+loop.selection_idx
                                style:
                                  border: 1px solid lightgray
                                  border-radius: 5px
                      - component: f7-popup
                        config:
                          class: ="selectionPopup_"+loop.selection_idx
                        slots:
                          default:
                            - component: f7-page
                              slots:
                                default:
                                  - component: f7-navbar
                                    config:
                                      title: =loop.selection.title
                                      style:
                                        position: sticky
                                    slots:
                                      left:
                                        - component: oh-link
                                          config:
                                            iconF7: arrow_left
                                            popup-close: true
                                  - component: f7-list
                                    config:
                                      virtualList: true
                                    slots:
                                      default:
                                        - component: oh-repeater
                                          config:
                                            sourceType: itemStateOptions
                                            itemOptions: =loop.selection.item
                                            for: option
                                            fragment: true
                                          slots:
                                            default:
                                              - component: oh-list-item
                                                config:
                                                  title: =loop.option.label
                                                  actionFeedback: ='{ "text":"Ha seleccionado <b>' + loop.option.label + '</b><br>Sending command <b>' +  loop.option.value + '</b> to item", "position":"center","icon":"<i style=\\"color:green\\" class=\\"f7-icons\\">checkmark_alt</i>","closeButton":true }'
                                                  noChevron: true
                                                  radio: true
                                                  checked: "=loop.option.value === items[loop.selection.item].state ? true : false"
                                                  popupClose: true
                                                  action: command
                                                  actionItem: =loop.selection.item
                                                  actionCommand: =loop.option.value

Any recommendation?

OK, it looks like my initial suggestion was probably the best choice. You now have the arrays set properly:

 - title: Planta
   icon: f7:placemark
   item: item_plants
 - title: Medida
   icon: f7:tag
   item: item_measures

Now replace both the instances of

items[loop.selection.item]

with

items[props[loop.selection.item]]

and both the instances of

=loop.selection.item

with

=props[loop.selection.item]

That should do the trick.

1 Like

I am looking forward to trying it and being able to tell you the result. In a few days I will be able to find out.

Thank you in advance.

With all due respect, you are the boss.

Thank you.

@JustinG First of all, thank you very much for your good answer. I would like to ask you if you could guide me so that I can save the two selected values in two different variables and also have the selected value / name appear in the arrows.

Excuse me for asking you such a direct question, I have had little success for a long time.

Thank you.

If I understand you correctly, you want the state of your item_plants item to show up just like your item_measures state. That goes back to this line:

after: =items[loop.selection.item].displayState

and the only problem with that line is that it looks like displayState is not defined for the item_plants item. If it is not defined for an item then the widget just uses an empty string "" so nothing is displayed.

Because of this, often when you are using a displayState you want to check if it exists first and then use the regular state if it does not (similar to your checked property in the popup).

after: "=(items[loop.selection.item].displayState) ? items[loop.selection.item].displayState : items[loop.selection.item].state"

If you are on the right track but I forgot to comment that my code has undergone an update.

Context: The items (“item_plants” and “item_measures”) have a series of options with their label = value.

  • I would like to be able to save the value of “item_plants” and “item_measures” in different variables for each when pressed and without having to change the state of the item (that is, without having to do action: command … somewhere) .

  • And the label of the options that appears as a value in after after having been clicked.

I am attaching code right now.
Thanks for everything and sorry for the inconvenience.

uid: widget_27f9a28a48
tags: []
props:
  parameters:
    - description: Title for the query widget
      label: Title
      name: widget_title
      required: true
      type: TEXT
    - context: item
      description: Plant for the query
      label: Plants
      name: item_plants
      required: true
      type: TEXT
    - context: item
      description: Measure for the query
      label: Measures
      name: item_measures
      required: true
      type: TEXT
  parameterGroups: []
timestamp: Dec 14, 2021, 3:12:35 PM
component: f7-card
config:
  class:
    - padding
  outline: true
  footer: ="PK = " + (dayjs().format('YYYY_MM_DD__HH_mm_ss_SSS')) + "__" + (vars.test_1)
slots:
  default:
    - component: Label
      config:
        text: =(props.widget_title)
        style:
          font-size: 16px
          font-weight: bold
    - component: f7-col
      slots:
        default:
          - component: oh-input-card
            config:
              clearButton: true
              sendButton: true
              required: true
              validate: true
              validate-on-blur: true
              title: Desde
              type: datetime-local
              variable: from_selected
              footer: '=(vars.from_selected) ? "Seleccionado = " + (vars.from_selected) : ""'
    - component: f7-col
      slots:
        default:
          - component: oh-input-card
            config:
              clearButton: true
              sendButton: true
              required: true
              validate: true
              validate-on-blur: true
              title: Hasta
              type: datetime-local
              variable: to_selected
              footer: '=(vars.to_selected) ? "Seleccionado = " + (vars.to_selected) : ""'
    - component: f7-row
      slots:
        default:
          - component: oh-repeater
            config:
              sourceType: array
              for: selection
              in:
                - title: Planta
                  icon: f7:placemark
                  item: item_plants
                  variable: plants_value
                - title: Medida
                  icon: f7:tag
                  item: item_measures
                  variable: measures_value
              fragment: true
            slots:
              default:
                - component: f7-col
                  slots:
                    default:
                      - component: oh-list
                        config:
                          id: list
                        slots:
                          default:
                            - component: oh-list-item
                              config:
                                title: =loop.selection.title
                                after: "=(items[loop.selection.item].displayState) ? items[loop.selection.item].displayState : items[loop.selection.item].state"
                                smartSelect: true
                                icon: =loop.selection.icon
                                iconColor: default
                                popupOpen: =".selectionPopup_" + loop.selection_idx
                                style:
                                  border: 0.1px solid lightgray
                                  border-radius: 4px
                      - component: f7-popup
                        config:
                          class: ="selectionPopup_" + loop.selection_idx
                          closeOnEscape: true
                        slots:
                          default:
                            - component: f7-page
                              slots:
                                default:
                                  - component: f7-navbar
                                    config:
                                      title: =loop.selection.title
                                      style:
                                        position: sticky
                                    slots:
                                      left:
                                        - component: oh-link
                                          config:
                                            iconF7: arrow_left
                                            popup-close: true
                                  - component: f7-list
                                    config:
                                      virtualList: true
                                    slots:
                                      default:
                                        - component: oh-repeater
                                          config:
                                            sourceType: itemStateOptions
                                            itemOptions: =props[loop.selection.item]
                                            for: option
                                            fragment: true
                                          slots:
                                            default:
                                              - component: oh-list-item
                                                config:
                                                  title: =loop.option.label
                                                  actionFeedback: ='{ "text":"Ha seleccionado <b>' + loop.option.label + '</b><br>Sending command <b>' +  loop.option.value + '</b> to item", "position":"center","icon":"<i style=\\"color:green\\" class=\\"f7-icons\\">checkmark_alt</i>","closeButton":true }'
                                                  noChevron: true
                                                  radio: true
                                                  checked: "=(vars.test_1) === (loop.option.value) ? true : false"
                                                  popupClose: true
                                                  action: variable
                                                  actionVariable: test_1
                                                  actionVariableValue: =loop.option.value

OK, that’s not too difficult. You can use expressions to build variable names just like you have many other strings throughout the widget (e.g., your popup class). In this case, it sounds like you just want one variable for item_plants and one for item_measures, so you can just:

actionVariable: ="option_select_" + loop.selection_idx

and you can access that variable anywhere by rebuilding that same string inside the[] of the vars object:

after: =vars["option_select_" + loop.selection_idx]

Functions beyond that however, get a little more difficult. For two reasons:

  1. There is no way to initialize a key in the vars object with default values. It will remain undefined until some component sets that key. So you will not see anything in the after slot of the plant and measures components until a selection is made. This can be solved in the exact same way as the solution above to displayState. You’ll need that everywhere the variable appears in a context where it must have a value.

  2. There is now nothing in your script that actually passes those option values to items for openHAB to work with. You will have to add another component with an action that sends a command to an item. If you want that to be the new information from both plant and measurements selections then that probably will have to be a proxy_item that triggers a rule and the rule will have to parse all the information out of the proxy_item value and pass the commands onto the items from there.

1 Like

Hi @JustinG,

Following your good indications I have managed to have the two dynamic variables. Regarding the title of the label, I have also managed to show it after having clicked on it. The latter, I have achieved it in a way that technically I know is not very correct but I have not been able to do anything better. The chosen solution has consisted of introducing the label in the variable with a separator in the middle, in this case an equal and later using a split, I attach lines in case someone is useful in the future.

  • Variable:
actionVariable: ="option_select_" + loop.selection_idx
actionVariableValue: =(loop.option.value) + "=" + (loop.option.label)

  • After label:
after: =((vars["option_select_" + loop.selection_idx]).split("=")[1])

The only problem I have is that the first time before selecting any value from the list the value undefined appears and I want a blank space to come out. I have tried in various ways to control it with logic but I can’t know how to match the value of the variable … I have tried with undefined but without success.
image

Approximate example:

after: '=((vars["option_select_" + loop.selection_idx]).split("=")[1]) === undefined ? "" : ((vars["option_select_" + loop.selection_idx]).split("=")[1])'

I do not know if you could indicate me with your good advice or maybe you know of someone.

Thank you once again.

Finally I will add my code in case someone can learn or use something.

uid: widget_27f9a28ewww8
tags: []
props:
  parameters:
    - description: Title for the query widget
      label: Title
      name: widget_title
      required: true
      type: TEXT
    - context: item
      description: Plant for the query
      label: Plants
      name: item_plants
      required: true
      type: TEXT
    - context: item
      description: Measure for the query
      label: Measures
      name: item_measures
      required: true
      type: TEXT
  parameterGroups: []
timestamp: Dec 16, 2021, 3:22:50 PM
component: f7-card
config:
  class:
    - padding
  outline: true
  footer: ="PK = " + (dayjs().format('YYYY_MM_DD__HH_mm_ss_SSS')) + "__" + ((vars["option_select_" + 0]).split('=')[0]) + "_" + ((vars["option_select_" + 1]).split('=')[0])
slots:
  default:
    - component: Label
      config:
        text: =(props.widget_title)
        style:
          font-size: 16px
          font-weight: bold
    - component: f7-col
      slots:
        default:
          - component: oh-input-card
            config:
              clearButton: true
              sendButton: true
              required: true
              validate: true
              validate-on-blur: true
              title: Desde
              type: datetime-local
              variable: from_selected
              footer: '=(vars.from_selected) ? "Seleccionado = " + (vars.from_selected) : ""'
    - component: f7-col
      slots:
        default:
          - component: oh-input-card
            config:
              clearButton: true
              sendButton: true
              required: true
              validate: true
              validate-on-blur: true
              title: Hasta
              type: datetime-local
              variable: to_selected
              footer: '=(vars.to_selected) ? "Seleccionado = " + (vars.to_selected) : ""'
    - component: f7-row
      slots:
        default:
          - component: oh-repeater
            config:
              sourceType: array
              for: selection
              in:
                - title: Planta
                  icon: f7:placemark
                  item: item_plants
                  variable: plants_value
                - title: Medida
                  icon: f7:tag
                  item: item_measures
                  variable: measures_value
              fragment: true
            slots:
              default:
                - component: f7-col
                  slots:
                    default:
                      - component: oh-list
                        config:
                          id: list
                        slots:
                          default:
                            - component: oh-list-item
                              config:
                                title: =loop.selection.title
                                after: '=((vars["option_select_" + loop.selection_idx]).split("=")[1]) === undefined ? "" : ((vars["option_select_" + loop.selection_idx]).split("=")[1])'
                                smartSelect: true
                                icon: =loop.selection.icon
                                iconColor: default
                                popupOpen: =".selectionPopup_" + loop.selection_idx
                                style:
                                  border: 0.1px solid lightgray
                                  border-radius: 4px
                      - component: f7-popup
                        config:
                          class: ="selectionPopup_" + loop.selection_idx
                          closeOnEscape: true
                        slots:
                          default:
                            - component: f7-page
                              slots:
                                default:
                                  - component: f7-navbar
                                    config:
                                      title: =loop.selection.title
                                      style:
                                        position: sticky
                                    slots:
                                      left:
                                        - component: oh-link
                                          config:
                                            iconF7: arrow_left
                                            popup-close: true
                                  - component: f7-list
                                    config:
                                      virtualList: true
                                    slots:
                                      default:
                                        - component: oh-repeater
                                          config:
                                            sourceType: itemStateOptions
                                            itemOptions: =props[loop.selection.item]
                                            for: option
                                            fragment: true
                                          slots:
                                            default:
                                              - component: oh-list-item
                                                config:
                                                  title: =loop.option.label
                                                  noChevron: true
                                                  radio: true
                                                  checked: '=(loop.option.value) === (vars["option_select_" + loop.selection_idx]).split("=")[0] ? true : false'
                                                  popupClose: true
                                                  action: variable
                                                  actionVariable: ="option_select_" + loop.selection_idx
                                                  actionVariableValue: =(loop.option.value) + "=" + (loop.option.label)

The results of this are undefined because before the repeater runs, of course, loop itself is undefined. You can check if a variable is undefined, but you cannot check if an undefined variable has a property. So whenever you have situation where you refer to a property of a variable that may be undefined, you usually have to check if the variable itself exists first. The final result looks something like this:

after: '=((!!loop && !!loop.selection_idx) ? vars["option_select_" + loop.selection_idx].split("=")[1]) : ""'

Here !! is a double negation that give you true if the variable exists (and isn’t false) and , gives you false if the variable is any falsy value (undefined is a falsy value). So if loop doesn’t exist the condition is already false and doesn’t even evaluate the second half of the test so there’s no error about checking for the property of an undefined variable.

This approach is interesting but for some reason I am not understanding why it does not work. It keeps showing “udefined” instead of a space or something else. :frowning:

Sorry, I didn’t properly remove parens when I modified your line. The corrected code should be:

after: '=(!!loop && !!loop.selection_idx) ? vars["option_select_" + loop.selection_idx].split("=")[1] : ""'

You don’t have to apologize for anything. A small advance, now undefined disappears but nothing ever appears and in the case of “measure” it keeps appearing, it is rare.

In the second image i checked both values

That’s because I’ve been trying to solve the wrong problem… Hoo boy, it’s going to be one of those days apparently. :roll_eyes:

Let’s start over:
The result of

after: '=((vars["option_select_" + loop.selection_idx]).split("=")[1]) === undefined ? "" : ((vars["option_select_" + loop.selection_idx]).split("=")[1])'

is undefined because before you’ve have clicked on anything, the variable vars["option_select_" + loop.selection_idx] doesn’t exist yet. That by itself is not an issue the widget would just render that as a null or in this case an empty after slot. However, you try to call the split() method on that non-existent variable and this results in an undefined error because the parser doesn’t even know what type of variable that is (because it doesn’t exist) let alone if it has a split() method. So the test you need to run isn’t on whether vars["option_select_" + loop.selection_idx]).split("=")[1] is undefined the test is whether or not vars["option_select_" + loop.selection_idx] exists yet.

after: '=(vars["option_select_" + loop.selection_idx]) ? vars["option_select_" + loop.selection_idx])split("=")[1] : ""'

Well maybe that was my problem and I didn’t explain it well, sorry.

This last thing that you indicate to me was one of my attempts when you initially guided me. This still doesn’t work :frowning: surely there’s something missing … I just started a week ago with custom widgets … this is frustrating me.

The result is this now and I have selected both values and later they are not reflected either. How can I fix this OMG. Could it be a bug or maybe I am doing or understanding something wrong?

Thanks for your time and effort Justin.

Of course not, because I deleted the . instead of the ) next to it when when typing up that last edit…

                                                                                                    V
after: '=(vars["option_select_" + loop.selection_idx]) ? vars["option_select_" + loop.selection_idx])split("=")[1] : ""'

try this instead:

after: '=(vars["option_select_" + loop.selection_idx]) ? vars["option_select_" + loop.selection_idx].split("=")[1] : ""'
1 Like

You were right, I’m going to leave my widget here in case someone gets this far and is useful. It has the purpose of making the query of item measurements visually. This is just the visual principle, then I intend to add a run button to run a rule or an exec command … I don’t know, I have to investigate / think.

I think it may have interesting things for people like me who are starting with custom widget since there is a lot of variety of examples and things used in the code … oh-repeater, dates … radio list selects … etc.

Greetings Justin, thanks for all your help, you have made my development much easier.

uid: widget_27f9a28ewww8
tags: []
props:
  parameters:
    - description: Title for the query widget
      label: Title
      name: widget_title
      required: true
      type: TEXT
    - context: item
      description: Plant for the query
      label: Plants
      name: item_plants
      required: true
      type: TEXT
    - context: item
      description: Measure for the query
      label: Measures
      name: item_measures
      required: true
      type: TEXT
  parameterGroups: []
timestamp: Dec 17, 2021, 10:16:58 AM
component: f7-card
config:
  class:
    - padding
  outline: true
  #footer: ="PK = " + (dayjs().format('YYYY_MM_DD__HH_mm_ss_SSS')) + "__" + ((vars["option_select_" + 0]).split('=')[0]) + "_" + ((vars["option_select_" + 1]).split('=')[0])
slots:
  default:
    - component: Label
      config:
        text: =(props.widget_title)
        style:
          font-size: 16px
          font-weight: bold
    - component: f7-col
      slots:
        default:
          - component: oh-input-card
            config:
              clearButton: true
              sendButton: true
              required: true
              validate: true
              validate-on-blur: true
              title: Desde
              type: datetime-local
              variable: from_selected
              footer: '=(vars.from_selected) ? "Seleccionado = " + (vars.from_selected) : ""'
    - component: f7-col
      slots:
        default:
          - component: oh-input-card
            config:
              clearButton: true
              sendButton: true
              required: true
              validate: true
              validate-on-blur: true
              title: Hasta
              type: datetime-local
              variable: to_selected
              footer: '=(vars.to_selected) ? "Seleccionado = " + (vars.to_selected) : ""'
    - component: f7-row
      slots:
        default:
          - component: oh-repeater
            config:
              sourceType: array
              for: selection
              in:
                - title: Planta
                  icon: f7:placemark
                  item: item_plants
                  variable: plants_value
                - title: Medida
                  icon: f7:tag
                  item: item_measures
                  variable: measures_value
              fragment: true
            slots:
              default:
                - component: f7-col
                  slots:
                    default:
                      - component: oh-list
                        config:
                          id: list
                        slots:
                          default:
                            - component: oh-list-item
                              config:
                                title: =loop.selection.title
                                after: '=(vars["option_select_" + loop.selection_idx]) ? vars["option_select_" + loop.selection_idx].split("=")[1] : ""'
                                smartSelect: true
                                icon: =loop.selection.icon
                                iconColor: default
                                popupOpen: =".selectionPopup_" + loop.selection_idx
                                style:
                                  border: 0.1px solid lightgray
                                  border-radius: 4px
                      - component: f7-popup
                        config:
                          class: ="selectionPopup_" + loop.selection_idx
                          closeOnEscape: true
                        slots:
                          default:
                            - component: f7-page
                              slots:
                                default:
                                  - component: f7-navbar
                                    config:
                                      title: =loop.selection.title
                                      style:
                                        position: sticky
                                    slots:
                                      left:
                                        - component: oh-link
                                          config:
                                            iconF7: arrow_left
                                            popup-close: true
                                  - component: f7-list
                                    config:
                                      virtualList: true
                                    slots:
                                      default:
                                        - component: oh-repeater
                                          config:
                                            sourceType: itemStateOptions
                                            itemOptions: =props[loop.selection.item]
                                            for: option
                                            fragment: true
                                          slots:
                                            default:
                                              - component: oh-list-item
                                                config:
                                                  title: =loop.option.label
                                                  noChevron: true
                                                  radio: true
                                                  checked: '=(vars["option_select_" + loop.selection_idx] && vars["option_select_" + loop.selection_idx].split("=")[0] === loop.option.value) ? true : false'
                                                  popupClose: true
                                                  action: variable
                                                  actionVariable: ="option_select_" + loop.selection_idx
                                                  actionVariableValue: =(loop.option.value) + "=" + (loop.option.label)

Hi again Justin,

I am writing to know if you, with your wisdom, would know how to define that in the “Floor Plan” and “Measure” list, before selecting a value, the first one in the list should be the default.
Thanks for your time as always.

Grateful, David.