Knockout - applying styles to the selected item in

2019-07-26 14:59发布

So this is piling libraries on top of libraries, but I'm not sure what else to do.

Our application has a number of drop-down elements all of which are Bootstrap Select objects. These replace the standard set of option tags inside the select with a complex series of other elements that give you much greater control over the styling of the children and make them searchable.

Most of these objects exist as reusable components with an HTML view and a Typescript ViewModel, bound together with Knockout.

A number of these menus have icons next to the text. This is handled with optionsAfterRender. Here's an example.

View:

<select
  data-bind="options: items, 
            value: selectedValue, 
            optionsText: 'value',
            optionsValue: 'id',
            selectPicker: {},
            optionsAfterRender: applyOptionAttributes">
</select>

ViewModel:

export default class SelectComponent {

selectedValue: KnockoutObservable<string>;
items: KnockoutObservableArray<SelectOption>

  constructor(koObservable: KnockoutObservable<string>) {
    // items fetched and bound
  }

  applyOptionAttributes(option: Node, item: SelectOption): void {
    ko.applyBindingsToNode(option, { attr: { "data-content": `<img src="${item.iconurl}" />`, title: item.value } }, item);
  }
}

interface SelectOption {
    value: string;
    id: string
    iconurl: string;
}

And this is fine. However, because of the way Bootstrap Select styles the items inside it, the icon is not applied to the currently selected item - it's only displayed when the user clicks on the menu and it pops up.

Now, of course, we have a requirement to display the icon in currently selected item too. But I don't know how to get that element to bind to it. I can't fetch it directly because of the view-viewmodel pattern. It doesn't seem to be among the nodes passed by optionsAfterRender.

How can I get hold of it to style it?

EDIT: pretty sure this is a bug in bootstrap-select. Have raised an issue

https://github.com/snapappointments/bootstrap-select/issues/2129

1条回答
做自己的国王
2楼-- · 2019-07-26 15:24

You could try to fix this by preventing the post-processing of the options:

  1. Replace the options binding with a foreach binding in the select element, binding each option with the appropriate ko-bindings.
  2. Make sure the selectPicker binding has its descendant bindings bound before calling selectpicker on the element.
  3. Profit!

ko.bindingHandlers['selectPicker'] = {
    init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
      ko.applyBindingsToDescendants(bindingContext, element);
      $(element).selectpicker();
      return { controlsDescendantBindings: true };
    }
}

ko.applyBindings({
    selectedValue: ko.observable(3),
    options: [
      { id: 1, name: 'Mustard', dc: '<span class="badge badge-warning">Mustard</span>' },
      { id: 3, name: 'Ketchup', dc: '<span class="badge badge-danger">Ketchup</span>' },
      { id: 4, name: 'Relish', dc: '<span class="badge badge-success">Relish</span>' }
    ]
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.13.3/css/bootstrap-select.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.13.3/js/bootstrap-select.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>

<select data-bind="selectPicker, value: selectedValue">
    <!-- ko foreach: options -->
    <option data-bind="text: name, attr: { value: id, 'data-content': dc }"></option>
    <!-- /ko -->
</select>

查看更多
登录 后发表回答