Dynamically show and hide items with oh:repeater

I’m trying to use oh:repeater dynamic to display only certain items. This basically works. But when the state changes, the items aren’t hidden or shown accordingly.

I’ve tried this both using the filter in oh:repeater and using visible on the item. Neither works.

Do you have any idea how to make this work?


“Fenster Küche” should be hidden when closed and vice versa.

Code:

uid: open_windows
tags:
  - card
  - openState
props:
  parameters:
    - description: A text prop
      label: Prop 1
      name: prop1
      required: false
      type: TEXT
    - context: item
      description: An item to control
      label: Item
      name: item
      required: false
      type: TEXT
  parameterGroups: []
timestamp: Jun 5, 2025, 11:05:49 PM
component: f7-card
config:
  title: geöffnete Fenster
slots:
  default:
    - component: oh-list
      slots:
        default:
          - component: oh-repeater
            config:
              for: item
              fragment: true
              groupItem: gruppeFensterKontakte
              sourceType: itemsInGroup
            slots:
              default:
                - component: oh-toggle-item
                  config:
                    color: '=(items[loop.item.name].state == "OPEN") ? "red" : "gray"'
                    icon: material:window
                    iconColor: '=(items[loop.item.name].state == "OPEN") ? "red" : "green"'
                    item: =loop.item.name
                    title: =loop.item.label
                    visible: =loop.item.state != "CLOSED"

I don’t use visibility. I just use the filter. And I don’t do anything special to make the entries appear or disappear without a refresh.

Here’s the code (minus the properties) for my battery widget that only shows those Items below a certain threshold:

component: oh-list-card
config:
  title: '=props.title + ": " + ((items[props.minLevel].displayState ===
    undefined) ? items[props.minLevel].state :
    items[props.minLevel].displayState)'
slots:
  default:
    - component: oh-repeater
      config:
        filter: Number.parseFloat(items[loop.item.name].state) <= props.max
        for: item
        fragment: true
        groupItem: =props.minLevel
        sourceType: itemsInGroup
      slots:
        default:
          - component: oh-list-item
            config:
              badge: "=(items[loop.item.name].displayState === undefined) ? loop.item.state :
                items[loop.item.name].displayState"
              badgeColor: '=(Number.parseFloat(loop.item.state) > props.green) ? "green" :
                (Number.parseFloat(loop.item.state) > props.orange) ? "orange" :
                "red"'
              icon: '=(Number.parseFloat(loop.item.state) > props.green) ? "f7:battery_100" :
                (Number.parseFloat(loop.item.state) > props.orange) ?
                "f7:battery_25" : "f7:battery_0"'
              iconColor: '=(Number.parseFloat(loop.item.state) > props.green) ? "green" :
                (Number.parseFloat(loop.item.state) > props.orange) ? "orange" :
                "red"'
              item: =loop.item.name
              title: =loop.item.label

What gets shown is solely controlled by the filter.

Here is my service status widget. Only those services that are offline (i.e. OFF) get shown.

component: f7-card
config:
  title: '=(items[props.rollup].state == "ON") ? props.online_title :
    props.offline_title'
slots:
  default:
    - component: oh-list
      slots:
        default:
          - component: oh-repeater
            config:
              filter: items[loop.item.name].state != "ON" &&
                !(items[loop.item.name].state.includes("OL"))
              for: item
              fragment: true
              itemTags: =props.tags
              sourceType: itemsWithTags
            slots:
              default:
                - component: oh-list-item
                  config:
                    badge: OFFLINE
                    badgeColor: red
                    icon: f7:wifi_slash
                    iconColor: red
                    item: =loop.item.name
                    title: =loop.item.label

I’ve already posted my lights widget but I’ll post it again here for another example.

component: f7-card
config:
  title: Lights
slots:
  default:
    - component: oh-list
      slots:
        default:
          - component: oh-repeater
            config:
              fragement: true
              for: item
              sourceType: itemsWithTags
              itemTags: Switch,Light
              filter: loop.item.label.includes("Christmas") == false ||
                items.TisTheSeason.state == "ON"
            slots:
              default:
                - component: oh-toggle-item
                  config:
                    icon: f7:lightbulb
                    iconColor: '=(loop.item.state == "ON") ? "yellow" : "gray"'
                    color: '=(loop.item.state == "ON") ? "yellow" : "gray"'
                    title: =loop.item.label
                    item: =loop.item.name

When I toggle on “Tis the Season” all the Christmas Items immediately appear. When I toggle it off the Christmas Items disappear.

The only thing I can think of which might cause problems if you toggled the Item OFF and expected it to disappear. I can see that if you interact with an Item that Item won’t disappear even if it no longer meets the filter criteria. In my widgets above, all the Items that appear or disappear change state outside of the widget.

Note, I have no memory as to why I use oh-list-card as the top component for one, and f7-card for the others.

Your problem is the visible line. In the other expressions, you’ve used items[ITEM_NAME].state, but in this one you’ve used loop.item.state.

There is an important difference between these two. When the repeater gets the information about an item, that includes the state of the item at that moment, but the repeater stores that information in a static object. If the state of the item changes, the repeater’s loop variable DOES NOT change. This is one reason the items object exists. This is a special object that tracks the specifically needed items for the widget and DOES reflect all changes to those item states in real time. So whenever you need an expression that changes with an item state, even inside a repeater, you have to use the items object.

To get the name of the item to use with the items object inside a repeater, you do need to rely on the loop variable, so the final expression looks like this:

visible: =items[loop.item.name].state != "CLOSED"
2 Likes

That’s it. Thank you!