Reputation: 303
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
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