Navigate to new page in multi-page app using link in aggrid cell

I am trying to navigate between pages in a multi-page app based on a click of a link in an aggrid cell to create a drill-through. I have it working code below. This works fine as long as linkElement.target = “_blank”, but I need it to replace the current top-level page (“_top”). This does not work as the aggrid is in an iframe.
“The frame attempting navigation of the top-level window is sandboxed, but the flag of ‘allow-top-navigation’ or ‘allow-top-navigation-by-user-activation’ is not set.”

Is there a way to set the sandbox attributes for the iframe?
Is there a simpler way to achieve this, maybe through backend python code?

link_jscode = JsCode('''
    function(params) {
    var element = document.createElement("span");
    var linkElement = document.createElement("a");
    var linkText = document.createTextNode(params.value);
    link_url = 'http://localhost:8501/Details?task_name=MY_TASK';
    linkElement.appendChild(linkText);
    linkText.title = params.value;
    linkElement.href = link_url;
    linkElement.target = "_top";
    element.appendChild(linkElement);
    return element;
  };
  ''')
...
gb.configure_column("link", cellRenderer=link_jscode)

Hi friends, I had a similar problem. And use a piece of js code to modify the sandbox property of the iframe. But since I want the modification to be executed as soon as the page is ready, the actual effect is not satisfactory. Your requirement is to trigger on click, I think it should work. Can you try this code?
BTW, I used jQuery to select this iframe. You can use JS selector as soon.

$(document).ready(function(){
    $("iframe", window.parent.document).each(function(){
        const st_iframe = $(this);
        st_iframe.attr('sandbox', 'allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts allow-downloads allow-top-navigation allow-top-navigation-by-user-activation');
    });
});

I was able to achieve this using a combination of a few hacky bits of code - Very open to suggestions for improvements.

First, use the streamlit html component to embed an iframe at the bottom of your app which contains some custom JS Code to ‘un-sandbox’ any AgGrid iframes present in your app. JS Code below. I found the call to .reload() the iframe was necessary for the updated attributes to ‘take hold’.

from streamlit.components.v1 import html
from st_aggrid import AgGrid, JsCode, GridOptionsBuilder as GOB

iframe_jscode = '''
var grid_frames = parent.document.querySelectorAll("iframe[title='st_aggrid.agGrid']")
for (var i = 0; i < grid_frames.length; i++) {
        grid_frames[i].setAttribute("sandbox", "allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts allow-downloads allow-top-navigation-by-user-activation allow-top-navigation");
        grid_frames[i].contentWindow.location.reload();
}
'''

After that, the code you posted above will work with target = “_top” however clicking the link in AgGrid triggers a reload of your browser tab so although the second page loads, it doesnt have access to the state from the first page.

I found you could sort of get around that by constructing the link in your cellRenderer function with query parameters based on the data in each row (accessible through params.node.data in the func), or if maintaining session state is a must then I found a solution using an onCellClicked callback as follows.

The callback adds query parameters to the url of your app based on the data in the row of the clicked cell and then searches through the links in the sidebar to find the one which points to your desired page. When it does, it simulates a click event on the sidebar link using .click() which changes the page without losing the session state.

I don’t think the ‘back’ button works properly to go back a page with this solution which I think is to do with my use of history.pushState(), but hopefully this is helpful

cell_click_callback = JsCode('''
function(event) {
    var mainApp = window.parent.document;
    var arrSidebarLinks = mainApp.querySelectorAll("div[data-testid='stSidebarNav'] li a");
    const url = new URL(window.parent.window.location.href);
    url.searchParams.set('row', event.node.data.Flavor);
    for (var i = 0; i < arrSidebarLinks.length; i++) {
        var currLink = arrSidebarLinks[i];
        var currName = currLink.querySelector("span").innerHTML;
        if (currName == 'second page') {
            window.parent.window.history.pushState(null, '', url);
            currLink.click()
        };
    };
};
''')

data = pd.DataFrame({'Flavor' : ['Vanilla', 'Chocolate', 'Strawberry', 'Mint Chocolate'], 'Select Favorite' : 'me!', 'me!', 'me!', 'me!']})
go = GOB.from_dataframe(data)
go.configure_column('Select Favorite', onCellClicked=cell_click_callback)
go = go.build()
response = AgGrid(dataframe=data, gridOptions=go, allow_unsafe_jscode=True, theme='dark', fit_columns_on_grid_load=True)
html(f"<script language=\"javascript\">{iframe_jscode}</script>")

I did get the iframe properties set with your first snippet. Chrome and Edge still throw an error, and Safari just opens _top in a new page! I also tried setting the base property of the document to _top as suggested for Chrome, but no effect either.

# Allow iframes to open top-level windows
markup = '''
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script>
$(document).ready(function(){
    $("iframe", window.parent.document).each(function(){
        const st_iframe = $(this);
        st_iframe.attr('sandbox', 'allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts allow-downloads allow-top-navigation allow-top-navigation-by-user-activation');
    });
    $("base").attr("target", "_top");
    alert('iframe tweaked!!!');
});
</script>
'''
components.html(markup)```
<iframe
...
src="http://localhost:8501/component/st_aggrid.agGrid/index.html?streamlitUrl=http%3A%2F%2Flocalhost%3A8501%2F"
...
sandbox="allow-forms
allow-modals
allow-popups
allow-popups-to-escape-sandbox
allow-same-origin
allow-scripts
allow-downloads
allow-top-navigation
allow-top-navigation-by-user-activation"
title="st_aggrid.agGrid">
</iframe>
1 Like