How to initialize a property based on another property in a custom widget?

I’ve created a custom “select” widget which has a select_all property that can be used to
initialize the widget with all its options selected already. In this case, I would like to set the value
property to a list of all the options instead of empty list (its default value).
I tried the following:

if (this.model.select_all) {
  this.model.setv({value: this.all_values}, {silent: true})
}

but when I deselect one of the options (all the options are selected in the beginning) the value callback (on the python side) says that the old value is an empty list instead of a list with all the options.
Is there a way to silently initialize the value property based on the select_all property so it will take effect also on the python side?

The {silent: true} is suppressing change events that would synchronize to Python (that is explicitly the purpose that setting that option is for)

I see, so if I understood correctly, currently there’s no way of setting the initial value of value based on the value of select_all, right?

Did you try without the silent option?

then it invokes the python callback and I don’t want this to happen since it’s supposed to be the initial value…

Can the default value not be initialized on the python side in the property definition to start?

You mean to set the default value of value based on the value of select_all? If so, I don’t think I have access to the values of the properties in the widget’s definition file. When I check the properties type it says bokeh.core.property.*. Is it possible to access the actual values?

@roinr I’m not sure I understand your question. I was suggesting specifying default values the same way Bokeh does itself for its own models. If a new property on a class:

Or if overriding an existing property on a subclass:

As always, it would be vastly easier to provide concrete suggestions if there was a Minimal Reproducible Example to focus the discussion.

@Bryan Thanks for the reply and sorry for the delay.
My question is whether it’s possible to set the default value of a property based on the value of a different property?
Let’s say we have created a custom Div widget. This widget has two properties (other than the ones it inherits): words and select_all. words is just a list of strings. select_all controls whether all the words in the list will be displayed (True) or just the first word (False). In the provided MRE, I set the select_all to True and I expect that the widget will set the value of text to Hello World automatically. In order to check this behavior, I added a button that changes the text. The on_change callback then prints the old and new values of the text property. The expected behavior is that the old value will be “Hello World” since the select_all was set to True. As you can see, my attempt was to silently set the text based on the value of select_all in the initialize method which obviously doesn’t work but I do want to change text silently since this should be its initial value and shouldn’t invoke the callback.

MRE:

from bokeh.models.layouts import Column
from bokeh.models.widgets.markups import Markup
from bokeh.core.properties import String, List, Bool
from bokeh.util.compiler import TypeScript
from bokeh.models import Button
from bokeh.io import curdoc

TS_CODE = """
import {Markup, MarkupView} from "models/widgets/markup"
import * as p from "core/properties"

export class CustomDivView extends MarkupView {
  model: CustomDiv
 
  initialize(): void {
    super.initialize()
    super.render()

    const initText = this.model.select_all ? this.model.words.join(' ') : this.model.words[0];
    this.model.setv({text: initText}, {silent: true})
  }

  render(): void {
    super.render()
    this.markup_el.textContent = this.model.text
  }
}

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

  export type Props = Markup.Props & {
    words: p.Property<string[]>
    select_all: p.Property<boolean>
  }
}

export interface CustomDiv extends CustomDiv.Attrs {}

export class CustomDiv extends Markup {
  properties: CustomDiv.Props
  __view_type__: CustomDivView

  constructor(attrs?: Partial<CustomDiv.Attrs>) {
    super(attrs)
  }

  static init_CustomDiv(): void {
    this.prototype.default_view = CustomDivView

    this.define<CustomDiv.Props>(({Boolean, String, Array}) => ({
      words:          [ Array(String), [] ],
      select_all:     [ Boolean, false ],
    }))
  }
}
"""


class CustomDiv(Markup):
    ''' Custom Div widget.
    '''
    __implementation__ = TypeScript(TS_CODE)

    words = List(String, default=[], help="""
    List of words.
    """)

    select_all = Bool(default=False, help="""
    Displays all the words in the list.
    """)


div = CustomDiv(words=["Hello", "World"], select_all=True)
b = Button(label="change text")


def on_change(attr, old, new):
    print(f"old: {old},", f"new: {new}")


def on_click(event):
    div.text = "Hello"


div.on_change('text', on_change)
b.on_click(on_click)

curdoc().add_root(Column(div, b))

I don’t think so. It is possible to specify a function for computing/providing a default value, but this happens before initialization, so the function would not be able to refer to other properties. The Bokeh object/event model is not set up for this, in particular because the order-of-initialization of properties makes no guarantees, and exposing that order to users seems like a nightmare waiting. I have to be honest and state that I doubt a cost/benefit analysis of the complexity necessary to add this would come out favorably. Default values are purely and only a static “compile time” (or at least, pre-initialization) concept.

@Bryan I understand…thanks!
Just making sure, there’s no way to achieve what I’m looking for. Meaning, updating a property on the typescript end in a way that it will take effect also on the python end but won’t invoke the property’s callback, right?

Meaning, updating a property on the typescript end in a way that it will take effect also on the python end but won’t invoke the property’s callback, right?

Correct. A silent update will suppress callbacks but will also suppress sync to Python. I’m not aware of a way to update Python without triggering a callback. cc @mateusz just in case there is some development I am not aware of, though.