openHAB3 UI | How to write a custom widget with multiple slots

Hey everybody,

I’m trying to write a custom widget which shall expose two slots (header and content) like it’s done by some of the oh-* or f7-* widgets (e.g. f7-card expose header, content and a footer slot)

I’ll intend to use it in another widget like this:

    - component: widget:my-component
      slots:
        header:
          - component: Label
            config:
              text: 'Hello'
        content:
          - component: Label
            config:
              text: 'Some text'

Is this possible with custom widgets or limited to the provided oh-* or f7-* widgets?

Best regards
Jan

This does not make sense to me.
What are you trying to achieve?

I have a number of widgets, all with the same layout:

component: f7-block
config:
  style:
    background-color: "=props.bgcolor ? props.bgcolor : '#F7F7F7'"
    border-radius: var(--f7-card-expandable-border-radius)
    box-shadow: 0.25rem 0.25rem 0.5rem 0 rgba(0,0,0,0.1)
    margin: 0.25rem
    padding: 0.5rem 1rem
slots:
  default:
    - component: f7-block 
      config:
        style:
          align-items: center
          display: flex
          flex-direction: row
          margin: 0
          padding: 0
      slots:
        default:
          - component: oh-icon
            config:
              height: 1.25rem
              icon: <some icon>
              style:
                margin-right: 0.75rem
          - component: Label
            config:
              style:
                font-size: 1rem
              text: =props.title
    - component: f7-block
      config:
        style:
          margin: 0.75rem 0 0 0
          padding: 0
      slots:
        default:
        ... here comes the actual content ...

Its similar to the f7-card component, defining a card with a header and a content block and - most important - same spacing etc.

Instead of rewriting this snippet in every widget, I want to create one widget defining this layout, like the f7-card component, so I can use it the same way:

component: my-custom-card-widget
slots:
  header:
    - component: oh-icon
      config:
        height: 1.25rem
        icon: <some icon>
        style:
          margin-right: 0.75rem
    - component: Label
      config:
        style:
          font-size: 1rem
        text: =props.title
  content:
    ... here comes the actual content ...

With this solution I void avoid writing duplicate code in every widget. Furthermore I ensure every widget has the same layout and adjustments only need to be made once.

Yes it’s possible for exactly this sort of use case. Here’s a very basic example:

uid: demo:widget_with_slots
tags: []
props:
  parameters: []
  parameterGroups: []
component: f7-row
config:
  style:
    border: solid
slots: {}
uid: demo:use_widget_slots
tags: []
props:
  parameters: []
  parameterGroups: []
component: f7-block
config: {}
slots:
  default:
    - component: widget:demo:widget_with_slots
      slots:
        default:
          - component: oh-button
            config:
              text: Show this button

image

There are some limitations, of course. First, you cannot concatenate two slots together. If your template widget has a type of slot already defined it will get overwritten when you call that widget with a different version of that slot. If, in the example above, the demo:widget_with_slots also has a component in its default slot:

uid: demo:widget_with_slots
tags: []
props:
  parameters: []
  parameterGroups: []
component: f7-row
config:
  style:
    border: solid
slots:
  default:
    - component: oh-button
      config:
        text: This button will not be rendered

you will still get the same output as above because the This button will not be rendered component is overwritten by the Show this button component.

Second, you cannot create your own custom slot types. You are still limited to the slots that are available for your widget’s base component.

1 Like

Thanks for your reply :slight_smile:

The first restriction is not a problem, I expected it to be so.

However I had hoped that I could define some custom slot types. But at least I can style the f7-card component once the way I prefer with your solution. That already makes it much easier for me.

That is very interesting - thanks Justin. Can you tell me, Can I pass props from the parent widget to the child?

One use case would be a widget that has a list with a repeater. The group item for the list is given as a prop for the parent widget. Each list item under the repeater uses a custom child widget, one for each member of a group over which the list is iterating. Each child widget therefore needs to know which group member item it should be displaying.

That would be very powerful and I have a need for exactly that in a custom widget I am working on.

EDIT:

Don’t worry - I think I’ve worked it out

Let’s say the parent has a prop called “item”. The child also a prop called “item”. In the parent widget the item (the group item here) would be =props.item. If we want to pass that to the child widget, we call the widget as Justin has outlined above but we give it a config parameter of item, with the value of =props.item.

That seems to work

Of course you can. Check the semanticHomeMenu widgets in the marketplace for example. The main widget is calling sub widgets with props values from a repeater.

Thanks Hans-Jörg. Wow! there’s a lot in there to digest. I hadn’t found that.

Understanding that is a project for another day but I think I have enough to go on.

1 Like