Open a Link on a New Tab

Hello! I’m doing a Dashboard and I want to release a new tab when I click on the scatterplot. I have already my code, but I don’t know how to give the link into a component that opens up this new tab. I tried by using html.Link and html.A

import dash
from dash import html
from dash import dcc
import pandas as pd 
import plotly.express as px
from dash.dependencies import Input,Output
import dash_bootstrap_components  as dbc
from scipy.stats import linregress


df = pd.read_csv('genres_v2.csv',sep=',')
df['duration_ms'] = df['duration_ms']*(0.001/60)
genre = df['genre'].unique()


app = dash.Dash(external_stylesheets=[dbc.themes.CYBORG])

app.layout = html.Div([
    html.Img(src=app.get_asset_url('spotify.jpg'), #introduccion de imagen desde carpeta 'assets'
             style={'height':'5%','width':'5%'}),
    html.H3('Siente tu Música preferida', #tipo de Header(es más pequeño que H1)
            style={'textAlign':'center','color':'#1DB954',
                   'font-family':'gotham','font-weight':'bold'}),
    html.Br(),
    html.Label('Características',
               style={'color':'white','font-weight':'bold'}),
    dcc.Dropdown(id='first_var', #escoger las variables que se van a correlacionar o no
                 options=[{'label':'Bailibilidad','value':'danceability'},
                          {'label':'Energía','value':'energy'},
                          {'label':'Sonoridad','value':'loudness'},
                          {'label':'Hablabilidad','value':'speechiness'},
                          {'label':'Acústica','value':'acousticness'},
                          {'label':'Instrumentalidad','value':'instrumentalness'},
                          {'label':'Viveza','value':'liveness'},
                          {'label':'Valencia','value':'valence'},
                          {'label':'Tempo','value':'tempo'}
                          ],
                 placeholder='Seleccionar variables de interés',
                 multi = True,
                 searchable= True),
    html.Br(),
    html.A(html.Button('Reset',style={'background-color':'#1DB954'
                                      ,'color':'white',
                                      'font-family':'gotham',
                                      'height':'10%','width':'10%',
                                      'font-weight':'bold'}),href='/'),   #Refresh la página para nuevo dropdown
    html.Br(),
    html.Label('Duración de la canción [min]',
               style={'color':'white','font-weight':'bold'}),
    dcc.RangeSlider(id='duration_slide',
                    min=df['duration_ms'].min(),
                    max=df['duration_ms'].max(),
                    value=[2.5,5.5],
                    tooltip={"placement": "bottom", "always_visible": True}, #instead marks
                    step=0.5),
    html.Br(),
    html.Label('Escoger un Género',
               style={'color':'white','font-weight':'bold'}),
    dcc.RadioItems(id='my_genre',
                   options =[{'label':i ,'value':i} for i in genre],
                   style={'color':'#1DB954'},
                   labelStyle={'display': 'inline-block','width':'12%'}), #para poder darle formato al los bullets
    
    html.Br(),
    dcc.Graph(id='Graph_cor'),
    dcc.Textarea(id='mytext',
                  style={'background-color':'black','color':'#1DB954','textAlign':'center',
                         'font-weight':'bold'},
                  disabled= True),
    
    html.Link(id='my_link',target='_blank')
    

    ])

@app.callback(
    Output(component_id='first_var', component_property='disabled'),
    Input(component_id='first_var', component_property='value')
    )

def disable_dropdown_one(val1): #permite solo dos inputs del dropdown 
    if len(val1) == 2:
        return True
    else:
        return False
    
@app.callback(Output(component_id='Graph_cor',component_property='figure'),
              Output(component_id='mytext',component_property='value'),
              Input(component_id='first_var',component_property='value'),
              Input(component_id='duration_slide',component_property='value'),
              Input(component_id='my_genre',component_property='value')
              )

def grafica_correlacion(variables,tiempo,genero):
    
    df1 = df[df['duration_ms']>=tiempo[0]]
    df1=df1[df1['duration_ms']<= tiempo[1]]
    df1 = df1[df1['genre']==genero]
    slope, intercept, r, p, stderr = linregress(df1[variables[0]],
                                                     df1[variables[1]])
    texto = 'La correlación es de {}'.format(r)
    fig = px.scatter(df1,x= variables[0],
                     y= variables[1],title = 'Correlación entre {} y {}'.format(variables[0],variables[1]),
                     color = 'mode',size='duration_ms',color_continuous_scale=px.colors.sequential.Greens,
                     hover_name='song_name',labels={'song_name':'Song'}) 
    fig.update_layout(plot_bgcolor='black',
                      paper_bgcolor='black', font={'color':'white'})
    
    fig.update_layout(transition_duration=500) #modificar y actualizar figura
    
    return fig, texto

@app.callback(Output(component_id= 'my_link', component_property = 'href'),
    Input(component_id= 'Graph_cor', component_property='clickData'))

def to_link (new_data):
    #print(new_data)
    list1 = new_data.get('points')
    dict1 = list1[0]
    
    
    songs_time = dict1.get('marker.size')
    hola = df[df['duration_ms']==songs_time]
    code = hola['id'].iloc[0] #select the code for a data frame
    
    link = "https://open.spotify.com/track/" + code
    
    return link 

    
    
app.run_server()

Hi there,

Welcome to the community!

I would imagine that there might be a way to combine n_clicks in a html.A with your callback to trigger a “fake click” on the link itself, but I am not sure it would work…

A (possibly) better alternative would be to do it with a clientside callback using the native window.open(url) method from JavaScript. The only caveat for your case is that you use a value from a dataframe on the server side to generate the URL, so you need somehow to make this data available in the clientside. This can be done via a dcc.Store component.

So, assuming that df is passed to the Store component (via df.to_dict("records")), your callback would look something like:

app.clientside_callback(
    """
    function(new_data, df_as_json) {
        // I will ignore that there might be some undefined 
        const songs_time = new_data.points[0].marker.size 
        
        // filter the data
        const hola = df_as_json.filter(row => row["duration_ms"] == songs_time)
        // get first id
       const code = hola[0]["id"]

       const link = `https://open.spotify.com/track/${code}`;

       // This is where the tab gets opened
      window.open(link);

       return link; // bogus return, the function runs for its side-effect
    }
    """,
    Output('my_link', 'href'),
    Input("Graph_cor", 'clickData'),
    State('df-store', 'data')
)

I tried to keep the js function as close as your python implementation, but there are some exceptions that needs to be handled. I hope you get the main point though… :slight_smile:

1 Like

Thanks a lot. I’ll try. I’m new here so…

Sure! Here are the docs for Clientside callbacks. It can be a bit tricky, but please reach out if you are struggling to get it done.