dash-core-components icon indicating copy to clipboard operation
dash-core-components copied to clipboard

A dcc.DocumentTitle Component for Updating the Page Title Dynamically

Open chriddyp opened this issue 5 years ago • 7 comments

This would enable users to update the tab:

  1. When navigating pages with dcc.Location
  2. When updating tabs with dcc.Tabs

I've helped a customer with a similar component with an implementation below:


import {Component} from 'react';
import PropTypes from 'prop-types';

export default class DocumentTitle extends Component {

    componentDidMount() {
        document.title = this.props.children;
    }

    componentWillReceiveProps(nextProps) {
        document.title = nextProps.children;
    }

    render() {
        return null;
    }
}

DocumentTitle.defaultProps = {
    children: 'Dash'
}

DocumentTitle.propTypes = {
    children: PropTypes.string,
    id: PropTypes.string
}

To bring this into the library, we would need to:

  1. incorporate that code as a new component
  2. then add some tests
  3. Modify componentWillReceiveProps to the non-deprecated way of doing things with React
  4. Add docs to:
    • dash.plotly.com/urls
    • dash.plotly.com/dash-core-comoponents/documenttitle. Provide a cross link to /external-resources to mention how to set the page title via the server side template, which would provide the page title on page load rather than dynamically. Doing this on page load would be better for SEO.
    • Provide a link to these docs from the external-resources page where we discuss how to set the title in the server-side template and mention the limitations of that method (it will only update the page title on page load, not on subsequent navigation via dcc.Link)

If any community member takes this on before we get around to, please comment below!

chriddyp avatar Jul 20 '20 17:07 chriddyp

As a workaround before we have this component, users could do a clientside callback with a side affect that would update document.title as a side effect. I have not tested this, but here is how it might work in principle:

app.layout = app.layout = html.Div([
    html.Div(id='dummy'), 
    dcc.Tabs(id='tabs-example', value='tab-1', children=[
        dcc.Tab(label='Tab one', value='tab-1'),
        dcc.Tab(label='Tab two', value='tab-2'),
    ]),
    html.Div(id='tabs-example-content')
])


app.clientside_callback(
    """
    function(tab_value) {
        document.title = tab_value;
        return null; // dummy output
    }
    """,
    Output('dummy', 'children'),
    [Input('tabs-example', 'value')]
)

chriddyp avatar Jul 20 '20 18:07 chriddyp

unfortunately the title change using the above workaround gets reverted as soon as the clientside callback exits as you can see by adding an alert:

import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output

app = dash.Dash(__name__)

app.layout = app.layout = html.Div([
    html.Div(id='dummy'),
    dcc.Tabs(id='tabs-example', value='tab-1', children=[
        dcc.Tab(label='Tab one', value='tab-1'),
        dcc.Tab(label='Tab two', value='tab-2'),
    ]),
    html.Div(id='tabs-example-content')
])
app.clientside_callback(
    """
    function(tab_value) {
        console.log(tab_value);
        document.title = tab_value;
        alert(document.title)
     }
    """,
    Output('dummy', 'children'),
    [Input('tabs-example', 'value')]
)

if __name__ == '__main__':
    app.run_server(debug=True, port=8074)

michaelbabyn avatar Jul 21 '20 21:07 michaelbabyn

Looks like we actually need to fix the title overriding behavior in Dash itself. Added a fix in https://github.com/plotly/dash/pull/1343

chriddyp avatar Jul 23 '20 17:07 chriddyp

OK, this feature is now available in dash==1.14.0. and is documented in http://dash.plotly.com/external-resources. See the "Update the Document Title Dynamically based off of the URL or Tab" example.

I'll keep this issue open to consider a dcc.DocumentTitle component which would enable a Python interface to updating the title. The current example documented above uses a clientside callback (JavaScript).

chriddyp avatar Jul 28 '20 20:07 chriddyp

I would like a dcc.DocumentTitle but your work around worked great for me in Dash==1.15.0

If someone finds this and wished they had a function to get a SPECIFIC url param, in this case subject, what's below worked for me.

In the python:

dcc.Location(id='url', refresh=True),
html.Div(id='blank-output'),

In the callbacks:

app.clientside_callback(
    """
    function(url) {
        function getQueryVariable(variable) {
            var query = window.location.search.substring(1);
            var vars = query.split('&');
            for (var i = 0; i < vars.length; i++) {
                var pair = vars[i].split('=');
                if (decodeURIComponent(pair[0]) == variable) {
                    return decodeURIComponent(pair[1]);
                }
            }
            console.log('Query variable %s not found', variable);
        }
            document.title = getQueryVariable('subject')
    }
    """,
    Output('blank-output', 'children'),
    [Input('url', 'search')]
)

CaseGuide avatar Sep 17 '20 13:09 CaseGuide

+1 for a dcc.DocumentTitle or an html.title - its docs already exist, but the component is non-functional. I'd like to be able to add this object to a page's layout object and have it override the global app title set in dash.Dash(title="Dash").

Using Dash 1.16.0, I haven't successfully gotten the "Update the Document Title Dynamically" clientside callback workaround to work for a dcc.Location. I suspect this is because my app is organized in multiple files, so this may be mitigated by 2.0's ability to make clientside callbacks from the dash module.

strongbad03 avatar Oct 19 '21 20:10 strongbad03

Hi there, I tried to set the "app.title" and the "app._favicon" for a monitoring app, searched and found this discussion. But I slightly disagree with your suggestion. From the view of the user (me), my fist attempt was to simply create the "app.id" attribute for the use as the output target with usual callback mechanism. This of course doesnt worked, but instead of creating more specialiced dcc function, this would be a way more compatible solution. First drawback: I dont know the dash code, so this maybe wont be easy to implement Second drawback: A consequence would be a change "app.layout" to the formal better "app.children" to make the app a proper parent. kr, Tx.

debilosaurus avatar Mar 26 '22 16:03 debilosaurus