How to initialize a property based on other properties without invoking the "on_change" callback?

I’ve created a customized Multiselect widget which includes a “Select All” option.
I’ve added a new property to the widget called select_all which can be used to select all the options.
In order to implement this behavior I need to set the value property to include all the possible options. The issue is that when I initialize the widget I don’t want to invoke the on_change callback that’s attached to the value property (in main.py).
Is there a way to evaluate the value property on initialization without triggering the on_change callback?

This is the relevant TypeScript code (without the View class):

export namespace CustomMultiSelect {
  export type Attrs = p.AttrsOf<Props>

  export type Props = InputWidget.Props & {
    value: p.Property<string[]>
    options: p.Property<(string | [string, string])[]>
    include_select_all: p.Property<boolean>
    select_all: p.Property<boolean>
  }
}

export interface CustomMultiSelect extends CustomMultiSelect.Attrs {}

export class CustomMultiSelect extends InputWidget {
  properties: CustomMultiSelect.Props
  __view_type__: CustomMultiSelectView

  constructor(attrs?: Partial<CustomMultiSelect.Attrs>) {
    super(attrs)
  }
  
  initialize(): void {
    super.initialize()
    if (this.select_all && this.options.length) 
      this.value = this.options.map(opt => isString(opt) ? opt : opt[0])
  }

  static init_CustomMultiSelect(): void {
    this.prototype.default_view = CustomMultiSelectView

    this.define<CustomMultiSelect.Props>(({String, Array, Tuple, Or, Boolean}) => ({
      value:                 [ Array(String), [] ],
      options:               [ Array(Or(String, Tuple(String, String))), [] ],
      include_select_all:    [ Boolean, false ],
      select_all:            [ Boolean, false ],
    }))
  }
}

I found out (from going through the bokehJS code) that there’s a method called .setv() that can be used for this purpose. This method can be used like the python .update() method to update multiple properties simultaneously.
In addition, there’s an option to make the changes “silent” (so it won’t invoke the on_change callback) by calling it like so: this.model.setv({value: newValue}, {silent: true})

@Bryan
Even though I was glad to find this method, I can’t help but wonder whether these JS methods are documented somewhere. It would have saved me a lot of time.

Not really, for a few reasons. For the first several years BokehJS was entirely an implementation detail that users were not expected to interact with directly at all. Even now that there is some exposure via custom extensions and CustomJS callbacks, BokehJS is still under very active development and changing rapidly. Given all the many other priorities and limited resources, it’s just not been justifiable to spend a lot of effort documenting something that

  • affects a relatively very small number of Bokeh users
  • is likely to change and be out of date

All that said, we are certainly today interested in stabilizing and promoting BokehJS as a first class tool of its own, and so improving its documentation will be a necessary part of that. But speaking frankly, it will be slow going until/unless some opinionated frontend/JS developers decide to pitch in with contributions.

Thanks for the reply! yeah I hear you…makes sense after all.
Actually, since my colleagues and I use Bokeh pretty intensively, contributing seems inevitable.
BTW, chapeau for creating this!! We’re huge fans :metal:

1 Like