Oh-repeater with prop multiple: true

Hi,

i have a problem with oh-repeater and probably understanding the difference between the source types.

What i try to achieve is to be able to select multiple items and have them listed in a widget.
After spending a lot of time, searching online etc i tried a different approache with “itemsInGroup”
(This is a customized version of widget_f7-card_Temp_V2_Control btw)

    - context: item
      description: Contact Group
      label: Contact Group
      name: itemContactGroup
      required: false
      type: TEXT
      slots:
        default:
          - component: oh-list
            slots:
              default:
                - component: oh-repeater
                  config:
                    for: item
                    sourceType: itemsInGroup
                    groupItem: =[props.itemContactGroup]
                    fragment: true
                  slots:
                    default:
                      - component: oh-list-item
                        config:
                          icon: ='oh:' + loop.item.category
                          iconUseState: true
                          title: =loop.item.label
                          item: =loop.item.name
                          style:
                            color: black

When selecting a group (containing all contact items) i get this
image

This is kinda what i want. But i only want so select the specific items for the specific room

Here is the code i try to make work

    - context: item
      description: Door/Window contacts
      label: Contact Items
      name: itemContacts
      required: false
      type: TEXT
      multiple: true
      slots:
        default:
          - component: oh-list
            slots:
              default:
                - component: oh-repeater
                  config:
                    for: item
                    sourceType: array
                    in: =props.itemContacts
                    fragment: true
                  slots:
                    default:
                      - component: oh-list-item
                        config:
                          icon: ='oh:' + loop.item.category
                          iconUseState: true
                          title: =loop.item.label
                          item: =loop.item.name
                          style:
                            color: black

But this only gives me an empty list (can’t post more screenshots since i am new -_- )
Now the empty space below the Temp and Humidity will increase/decrease based on how many items i select.

I tried differnt ways of writing it but none gave me, for example, the “label”

If i change the title to

title: '=loop.item'

i get a list with the item names. (again sorry can’t post another screenshot)

loop.item.name / .state will return nothing. Now

title: '=items[loop.item].state'

will return the states. But i can’t seem to access category / label etc.

I am really lost here. Isn’t a multiple: true selection supposed to behave like a group?
What can i do to make this code work? Or does someone know a widget that already does this?

TIA
Mike

The problem here is that the itemsInGroup and the parameter array are producing very different output.

When the repeater uses itemsInGroup it fetches the information about the group’s children items from the API and that information is an object containing most of the standard information about an item which is why you can access the label and name properties, etc.

When you use the `multiple: true’ for the parameter, the array that is returned is just an array of the item name strings, not the complete item objects.

Hello,

ok. Thanks for the information.

Just a quick question then, because i am probably missunderstanding this as well.
You said it is an array of item name strings. Shouldn’t i be able to access the rest of the informations via

'=items[loop.item].XXX'

, where XXX is e.g. the label / state etc. directly then?

Because i will get the state, but not the label for example.

If ‘=items[name_of_item].XXX’ is the wrong way. Is there a way to access item informations that aren’t part of the widget?

The items object in the widgets is not direct access to all the information about items. That complete set of information can only come from API calls and those are expensive so widgets will only do so when forced to (for example, via one of the item-based repeater sources). The items object is only populated with an item’s state and (if it is different from the state) the state as formatted by any formatting properties the item may have (accessed as items[x].displayState).

Under normal circumstances, there is no way in the widgets to access an item’s label using the item’s name. If you look through many of the widgets examples available, you’ll see that often a widget with an item parameter will also have a label parameter because it’s just plain easier to have the user set that label property to whatever they want once when first adding the widget to a page than to try and extract the label information automatically.

This has been a known limitation of the custom widget system from the beginning, and is a consequence of the choices made to optimize performance of the widgets.

In the case of your widget, where the items are being dynamically added to a list, it is possible, of course, because the list of items can be derived from an API call if you use itemsInGroup or itemsWithTags. Instead of manually selecting the list of items, you need to find a way to group the items how you want them (or tag them) or use the filter property of the repeater to reduce the entire group to just the subset you are interested in. If you’ve built your contacts into the semantic model already, then the information you need for filtering may already be there.

Understood. Thank you very much for the in depth explenation. Now that i know this can’t be achieved, the way i tried it, i will look into the other options as you suggested.
I did play around a little bit with the semantic model. But haven’t dived into it to deep yet (still on my todo :slight_smile: )

Thank you very much again!

I’ll just leave my customized widget here if someone is interested.

I now made a group with all contacts (Windows/Doorfs) and filter them based on their name.
For example all my items in the room “Wohnzimmer” start with “WZ_”

So my “Filter ContactGroup String” is “WZ_”

With everything set it looks like this ( i set the heating valve position to 80% )

image

uid: widget_f7-card_Temp_V2_Control_mike
tags:
  - homekit-look
  - Soll Tag
  - in use
props:
  parameters:
    - description: Location?
      label: Location
      name: itemLocation
      required: false
      type: TEXT
    - default: thermometer
      description: Thermostate icon f7
      label: f7 icon name
      name: iconTemp
      required: false
      type: TEXT
    - default: drop
      description: Humidity icon f7
      label: f7 icon name
      name: iconHum
      required: false
      type: TEXT
    - context: item
      description: A Temperature item to display
      label: Item
      name: itemTemp
      required: false
      type: TEXT
    - context: item
      description: Target Temp label
      label: Target Temp label
      name: varTargetTempText
      required: false
      type: text
      default: "Target"
    - context: item
      description: A Set-Temperature item to display
      label: Item
      name: itemTempSoll
      required: false
      type: TEXT
    - context: item
      description: An Humidity item to display
      label: Item
      name: itemHum
      required: false
      type: TEXT
    - context: item
      description: the valve state
      label: Item
      name: itemValve
      required: false
      type: TEXT
    - context: item
      description: Door/Window contacts group
      label: Door/Window contacts group
      name: contactGroup
      required: false
      type: TEXT
    - description: Filter ContactGroup String
      name: contactGroupFilter
      required: false
      type: TEXT
  parameterGroups: []
timestamp: Jul 30, 2022, 12:15:41 AM
component: f7-card
config:
  style:
    noShadow: false
    class:
      - padding
    border-radius: var(--f7-card-expandable-border-radius)
    box-shadow: var(--f7-card-expandable-box-shadow)
    background-color: "=themeOptions.dark === 'dark' ? 'rgb(35, 35, 35)' : 'rgb(247, 247, 247)'"
    min-width: 9.5em
    max-width: 400px
    action: analyzer
    actionAnalyzerItems: =[props.itemTemp]
slots:
  content:
    - component: f7-block
      config:
        style:
          margin-left: -6px
          padding: -1px
          display: flex
          flex-direction: column
          align-items: start
    - component: f7-row
      config:
        class:
          - justify-content-left
      slots:
        default:
          - component: f7-icon
            config:
              f7: =props.iconTemp
              size: 22
              class:
                - align-self-center
              style:
                margin-left: -8px
                margin-top: 0px
                color: '=(Number.parseFloat(items[props.itemTemp].state.split(" ")[0]) <= 18.0) ? "blue" : (Number.parseFloat(items[props.itemTemp].state.split(" ")[0]) >= 27.0) ? "red" : (Number.parseFloat(items[props.itemTemp].state.split(" ")[0]) >= 24.0) && (Number.parseFloat(items[props.itemTemp].state.split(" ")[0]) <= 26.9) ? "orange" : "white"'
          - component: Label
            config:
              text: =props.itemLocation
              style:
                font-size: 14px
                font-weight: 500
                margin-top: 0px
                color: "=themeOptions.dark === 'dark' ? white : black"
    - component: f7-row
      config:
        class:
          - justify-content-left
      slots:
        default:
          - component: Label
            config:
              text: =(Number.parseFloat(items[props.itemTemp].state.split(" ")[0]) * 100 / 100).toFixed(1) + '°'
              style:
                font-size: 32px
                line-height: 1.1
                font-weight: 498
                margin-left: 0px
                margin-top: 0px
                color: "=themeOptions.dark === 'dark' ? white : black"
    - component: f7-row
      config:
        class:
          - justify-content-left
        visible: =props.itemHum !== undefined
      slots:
        default:
          - component: f7-icon
            config:
              f7: =props.iconHum
              size: 16
              style:
                top: 5px
                left: 0px
                color: '=(Number.parseFloat(items[props.itemHum].state.split(" ")[0]) > 60 && themeOptions.dark != "dark") ? "blue" : (themeOptions.dark === "dark" && Number.parseFloat(items[props.itemHum].state.split(" ")[0]) > 60) ? "lightblue" : "white"'
          - component: Label
            config:
              text: =(Number.parseFloat(items[props.itemHum].state.split(" ")[0]) * 100 / 100).toFixed(0) + '%'
              style:
                top: 5px
                left: 0px
                font-size: 13px
                font-weight: 500
                color: "=themeOptions.dark === 'dark' ? white : black"
    - component: f7-row
      config:
        class:
          - float-right
      slots:
        default:
          - component: oh-icon
            config:
              icon: oh:heating
              state: =items[props.itemValve].state
              visible: "=(props.itemValve != UNDEF) ? true : false"
              height: 30
              style:
                size: 20
                z-index: 2
                bottom: 75px
                border-radius: 25px 25px 25px 25px
                position: relative
          - component: f7-chip
            config:
              bgColor: '=(Number.parseFloat(items[props.itemTempSoll].state.split(" ")[0]) >= items[props.itemTemp].state.split(" ")[0]) ? "red" : "blue"'
              text: '=props.varTargetTempText + ": " + (Number.parseFloat(items[props.itemTempSoll].state.split(" ")[0]) * 100 / 100).toFixed(0) + " °C"'
              style:
                color: white
                size: 20
                z-index: 2
                bottom: 75px
                border-radius: 25px 25px 25px 25px
                margin-top: 2px
                right: -5px
    - component: f7-row
      config:
        visible: "=(props.contactGroup != UNDEF) ? true : false"
        class:
          - justify-content-left
      slots:
        default:
          - component: oh-list
            slots:
              default:
                - component: oh-repeater
                  config:
                    for: item
                    sourceType: itemsInGroup
                    groupItem: =[props.contactGroup]
                    fragment: true
                    filter: '=(props.contactGroupFilter != UNDEF) ? "loop.item.name.includes(props.contactGroupFilter) == true" : ""'
                  slots:
                    default:
                      - component: oh-list-item
                        config:
                          icon: ='oh:' + loop.item.category
                          iconUseState: true
                          title: '=(items[loop.item.name].displayState != UNDEF) ? loop.item.label + " " + items[loop.item.name].displayState : loop.item.label + " " + loop.item.state'
                          item: =loop.item.name
                          style:
                            color: "=themeOptions.dark === 'dark' ? white : black"
                            margin-left: -20px
    - component: oh-trend
      config:
        trendItem: =[props.itemTemp]
        style:
          --f7-theme-color-bg-color: transparent
          background: var(--f7-theme-color-bg-color)
          filter: opacity(40%)
          position: absolute
          width: 100%
          height: 80px
          top: 0
          left: 2
          z-index: 1
    - component: oh-link
      config:
        action: analyzer
        actionAnalyzerItems: =[props.itemTemp, props.itemHum]
        style:
          position: absolute
          left: 0px
          top: 0px
          height: 100%
          width: 100%