Dune
Dune

Reputation: 303

Using bootstrap-select from elm

A am trying to use bootstrap-select - a javascript/css library extending the html-select-tag with nice features and style. At first glance, calling it from elm seems simple. Indeed, the snipped

view : Model -> Html Msg
view model =
  select [ class "selectpicker", attribute "data-live-search" "true" ]
    [ option [] [ text "foo" ]
    , option [] [ text "bar" ]
    ]

yields a nice (searchable) select box with two items. However, things get complicated in dynamic situations. Suppose our elm model is a boolean deciding wether the select box is shown or not.

type alias Model = Bool

init : Model
init = True

update : Msg -> Model -> Model
update Toggle model = not model

view : Model -> Html Msg
view model =
  if model then
    div []
    [ select [ class "selectpicker", attribute "data-live-search" "true" ]
        [ option [] [ text "foo" ]
        , option [] [ text "bar" ]
        ]
    , button [ onClick Toggle ] [ text "toggle" ]
    ]
    else
      button [ onClick Toggle ] [ text "toggle" ]

When loading the page, we see again a nice select box which disappears when hitting the toggle button. However, when hitting the toogle button again, the select box will not appear again! The reason is that selectpicker nodes are required to be refreshed if content has changed (including enabling/disabling the node). That is, we have to call

$('.selectpicker').selectpicker('refresh');

from the outside Javascript world after our select box has been added to the DOM again.

I tried to solve that problem using ports, but unfortunately I only got elm to fire an event before rendering, so I additionally had to use setTimeout to wait for completion, which is quite hacky. I suppose there must be a neat solution using a custom element, but again, I was not able to figure out how to call the refresh function at the right moment.

Any help is greatly appreciated!

Upvotes: 1

Views: 314

Answers (1)

Dune
Dune

Reputation: 303

Finally, I managed to wrap bootstrap-select into a (minimal, nonperfect) custom element which automatically refreshes on updates. Here it is:

import { LitElement, html, customElement, property } from 'lit-element';
import * as $ from 'jquery';
import 'bootstrap';
import 'bootstrap-select';

@customElement('lit-select')
export class LitSelect extends LitElement {

    @property({ type : Array }) items = []

    updated() {
        $(this).find(".selectpicker").selectpicker('refresh');
    }

    createRenderRoot() {
        return this;
    }

    private renderItem(item: string) {
        return html`
            <option>
                ${item}
            </option>
        `;
    }

    render() {
        return html`
            <select class="selectpicker" data-live-search = "true">
                ${this.items.map(item => this.renderItem(item))}
            </select>
        `;
    }
}

This element can be created from HTML as

<lit-select items='["foo", "bar"]'></lit-select>

or from elm as

node "lit-select" [ attribute "items" "[\"foo\",\"bar\"]" ] []

and it also works in dynamic situations as above. However, an obvious drawback is that the item list has to be given to a lit-select attribute encoded as a json string. So the markup possibilities are rather limited (for example, the user cannot decide wether to give lit-select a bunch of options or a bunch of option groups).

I would be happy to see better solutions but since this is another topic, I will start a followup question soon.

Upvotes: 2

Related Questions