Confused on how to embed individual components of a server_document in different html divs

I’ve really been stuck trying to embed specific components from a flask app using “server_document” into a customized layout in html. Say I have a bokeh app that contains a plot and a button, where pressing the button will load data and then plot. How can I run server_document, then in my jinja2/html template, declare these components individually so that they can be rearranged on the page?

Put another way - I’d like to have the access to individual page components like when using the “script, div = components(plot)”, but I would like to have the functionality of the server so that they aren’t just static widgets. This means if I have two columns on a web page, one can house the button and the other can house the plot.

As it stands I can make a server_document for each widget I’d like to use, but this does not allow for the widgets to interoperate as in a single app (understandable behavior based on the docs). Is there a way to specify which server_document element goes where in the layout?

Working on Bokeh 1.2.0 currently

A few comments:

  • By far the easiest way to you use templates with a Bokeh server app is to make a Directory Style App with a templates subdirectory containing the Jinja template you want to use. There are way to do this besides making a Directory Style App, but they are somewhat more involved, so I am going to start on that assumption.

  • What should the template look like? There is a standard template you can use by overriding its pre-defined sections. If you can fit your use case in to this template, that will also be simplest. Then you can just follow the simple example there, and also the more more complete Dash example. In brief, you set a name property on your curdoc() roots, then in the template. you can call embed(roots.foo) to embed

  • If you can’t use that template, you will need to define the Jinja embed macro yourself, by hand. It looks like this:

    {% macro embed(obj) -%}
        <div class="bk-root" id="{{ obj.elementid }}"{% if obj.id %} data-root-id="{{ obj.id }}"{% endif %}></div>
    {%- endmacro %}
    

    I should say that I’ve never actually tried to do this personally, but it should be the way to get things done.

Thanks for the quick response Bryan!

I have used the directory style apps before as you suggested and those work well with the embed(roots) to get Bokeh widgets placed around on the page. However, I also want to embed this in flask so that if there is say a basic HTML widget that it can route data into the Bokeh app. Ie, there’s a basic html button that when clicked will load data from a computer or url, passes that to bokeh, bokeh plots the data on the plot. The problem I encounter with using the directory style app is that flask cannot read the jinja2 template and so only bokeh (not flask) is able to render the html properly.

The approach I’m currently using is having the bokeh app and flask app in the same document, with a function calling “server = Server({}, <io_loop>, )” and then I call the document as “python bokehAndFlaskApp.py”.

Is there a way to use jinja2 templating with the directory app while also using flask to route? Or is there an alternative strategy that I’m missing all together that can accomplish this?

So, I think the answer to this question is “Yes” but I am not actually sure offhand how to accomplish it. It will take a little investigation. I will try to look into it this week.

I am facing a problem similar to the one you struggled with @Nathan_Snyder . Therefore, I hope this is the right place to ask my question. However, this is my very first post, so I apologize if I made a mistake.

My goal is to set up an Apache reverse proxy on localhost which utilizes Flask and mod_wsgi to render a jinja template. In the template I make use of the embed(roots.xxx) syntax as described in Embedding Bokeh to place each Bokeh root element independently.

However, even without the Apache Webserver I encounter problems when establishing a connection between Flask and Bokeh.

My code works fine when serving the entire directory on localhost using

bokeh server flasktest

However, when starting the Bokeh server as
bokeh server flasktest --allow-websocket-origin=localhost:5000
and executing the Python Flask script
python __init__.py, I receive the error

jinja2.exceptions.UndefinedError: 'base' is undefined

I have already tried the following:

  • Copy the Bokeh default Jinja template into the templates folder
    (named base.html) and import it as {{% extends "base.html" %}}.
  • My very first attempt included starting the server programmatically as in the
    Bokeh howto example flask_embed.
    This, however, gives the same error.

My current directory structure is:

flasktest
--- templates
    --- index.html
--- __init__.py
--- __main.py

The relevant files for a minimal working example are:

#main.py:
from bokeh.models import ColumnDataSource
from bokeh.plotting import figure
from bokeh.io import curdoc
from bokeh.sampledata.sea_surface_temperature import sea_surface_temperature

df = sea_surface_temperature.copy()
source = ColumnDataSource(data=df)

plot = figure(x_axis_type='datetime',
              y_range=(0, 25),
              y_axis_label='Temperature (Celsius)',
              title="Sea Surface Temperature at 43.18, -70.43",
              name='plot')

plot.line('time', 'temperature', source=source)

curdoc().add_root(plot)
#__init__.py
from flask import Flask, render_template
from bokeh.embed import server_document

app = Flask(__name__)

@app.route('/', methods=['GET'])
def bkapp_page():
    script = server_document('http://localhost:5006/main')
    return render_template("index.html",
                           script=script,
                           template="Flask")

if __name__ == '__main__':
    app.run()

{% extends base %}
{% block contents %}
  <div> {{ embed(roots.plot) }} </div>
{% endblock %}

I appreciate all the advice you can give me!
Thank you!

I am having a similar problem to @Nathan_Snyder and @kalm in attempting to embed components of a bokeh app into a Flask webpage. Embedding the entire app works fine using server_session, but since the model parameter is no longer used (https://github.com/bokeh/bokeh/issues/8499), I can’t figure out how to embed a single component.

I attempted to use the advice that Bryan gave and define the embed macro myself,

...
{% macro embed(obj) -%}
    <div class="bk-root" id="{{ obj.elementid }}"{% if obj.id %} data-root-id="{{ obj.id }}"{% endif %}></div>
{%- endmacro %}
...

with the embedding html snippet looking something like

...
<div class="col">
{{ embed(roots.slider) }}
</div>
...

I then tried to pull a session as recommended in the docs but pass the roots to my template instead of the script that loads the entire app.

from flask import Flask, render_template

from bokeh.client import pull_session
from bokeh.embed import server_session

app = Flask(__name__)

@app.route('/', methods=['GET'])
def bkapp_page():
    
    url = "http://localhost:5006/sliders"

    # pull a new session from a running Bokeh server
    with pull_session(url=url) as session:
        
        # Can't pull a specific model
        # root_script = server_session(model=session.document.roots[0], 
        #                              session_id=session.id, url=url)

        roots = {'slider': session.document.roots[0]}

        # use the script in the rendered page
        return render_template("embed.html", roots=roots, template="Flask")

if __name__ == '__main__':
    app.run(port=8080)

However, there must be a step that I’m missing since no plots are displayed and obj.elementid is null in the embed macro. If this is the wrong approach entirely any advice would be welcome.

Thanks!

Hello,

Any solution or alternative for it??
I am facing same issue.

Thanks