ericrayanderson/shinymaterial

Programatically change selected side-nav-tab

Opened this issue ยท 8 comments

We need to programatically select a side nav tab. We tried to adapt the example from @bilbaoba 's example from this question regarding tabs: #79 but cannot get it to work.

Here is what we have tried:

library(shiny)
library(shinymaterial)

ui <- material_page(
	title = "Basic Page + Side-Nav with Tabs",
	nav_bar_fixed = TRUE,
	
	material_side_nav(fixed = TRUE,
										
	material_side_nav_tabs(
		side_nav_tabs = c(
			"Example Side-Nav Tab 1" = "example_side_nav_tab_1",
			"Example Side-Nav Tab 2" = "example_side_nav_tab_2"
		),
		icons = c("cast", "insert_chart")
	)),
	
	material_side_nav_tab_content(
		side_nav_tab_id = "example_side_nav_tab_1",
		
		material_button(input_id = "button", label = "GO TO SECOND SIDE NAV TAB")
		
	),
	
	material_side_nav_tab_content(
		side_nav_tab_id = "example_side_nav_tab_2",
		
		tags$h1("Second Side-Nav Tab Content"))
)

server <- function(input, output, session) {
	
	observe({
		if (input$button == 0)
			return()
		js_code <-
			"$('li.shiny-material-side-nav-tab a[href$=\"#example_side_nav_tab_2_tab_id\"]:first').trigger(\"click\");"
		session$sendCustomMessage(type = "shinymaterialJS", js_code)
	})
	
}
shinyApp(ui = ui, server = server)

Any help on this would be much appreciated. Thank you
(I did ask this on SO too https://stackoverflow.com/questions/56789753/programatically-change-selected-nav-tab-in-r-shinymaterial-app but had no response)

I once wrote a JS function to select a normal (not side nav) tab:

    shinyjs.select_material_TopTab = function(tab_id){
        var tab = $("ul.tabs li.tab a[href$=" + tab_id + "]");
            
        tab.trigger("click");
}

in the UI:

    shinyjs::useShinyjs(),
    shinyjs::extendShinyjs(text = jsCode, functions = c("select_material_TopTab")),

and in the server.R
js$select_material_tab("tab1") # change the id (without '_tab_id' at the end)
Maybe this can be adapted to the side nav bar?

Hi @Seyphaton thanks so much for this. I have tried to build into my example but with no luck so far. Any thoughts on what I could try?

library(shiny)
library(shinyjs)
library(shinymaterial)

jsCode <-
	'select_material_Tab = function(tab_id){
var tab = $("ul.tabs li.tab a[href$=" + tab_id + "]");

tab.trigger("click");
}'

ui <- material_page(
	title = "Basic Page + Side-Nav with Tabs",
	nav_bar_fixed = TRUE,
	
	shinyjs::useShinyjs(),
	shinyjs::extendShinyjs(
		text = jsCode,
		functions = c("select_material_Tab")
	),
	
	material_side_nav(fixed = TRUE,
										
	material_side_nav_tabs(
		side_nav_tabs = c(
			"Example Side-Nav Tab 1" = "example_side_nav_tab_1",
			"Example Side-Nav Tab 2" = "example_side_nav_tab_2"
		),
		icons = c("cast", "insert_chart")
	)),
	
	material_side_nav_tab_content(
		side_nav_tab_id = "example_side_nav_tab_1",
		
		material_button(input_id = "button", label = "GO TO SECOND SIDE NAV TAB")
		
	),
	
	material_side_nav_tab_content(side_nav_tab_id = "example_side_nav_tab_2",
																
	  tags$h1("Second Side-Nav Tab Content"))
)

server <- function(input, output, session) {

	observeEvent(input$button, ignoreInit = T, {

		session$sendCustomMessage(type = "shinymaterialJS", js$select_material_Tab("example_side_nav_tab_2"))
		
	})
	
}

shinyApp(ui = ui, server = server)


fungs commented

Hi everybody,
@ericrayanderson thanks for providing this package, despite some glitches and it's minimalistic footprint, it has really served me well.

I see the tab activation (including navbar) is an open issue which hasn't received a general implementation, yet. #79 apparently is tailored towards regular tabs and there currently isn't a way to generally active/switch tabs within the shinymaterial framework, without reverse-engineering and writing JavaScript code. I know some R but I'm definitely not a JavaScript developer.

Also, the function in commit f8c920d is not available in the latest release 0.5.0.

I can imagine two ways to integrate this:

  1. Using custom function as in regular Shiny, see updateTabsetPanel(). However, shinymaterial widgets lack an id field to control this.
  2. By registering callback functions for each tabset-like UI element to activate a tab, e.g. in the output object

Is there a chance to include a general solution? All I understand it would take adding a little JavaScript magic to the existing code.

Thanks!

Exactly @fungs, it was solved in the PR #81. I don't know why @ericrayanderson suppress the function.
I can rewrite it and make another PR. Maybe also implement other features of material tabs like disable.

fungs commented

Any work would be appreciated, I volunteer for testing :)

Hi @Seyphaton thanks so much for this. I have tried to build into my example but with no luck so far. Any thoughts on what I could try?

library(shiny)
library(shinyjs)
library(shinymaterial)

jsCode <-
	'select_material_Tab = function(tab_id){
var tab = $("ul.tabs li.tab a[href$=" + tab_id + "]");

tab.trigger("click");
}'

ui <- material_page(
	title = "Basic Page + Side-Nav with Tabs",
	nav_bar_fixed = TRUE,
	
	shinyjs::useShinyjs(),
	shinyjs::extendShinyjs(
		text = jsCode,
		functions = c("select_material_Tab")
	),
	
	material_side_nav(fixed = TRUE,
										
	material_side_nav_tabs(
		side_nav_tabs = c(
			"Example Side-Nav Tab 1" = "example_side_nav_tab_1",
			"Example Side-Nav Tab 2" = "example_side_nav_tab_2"
		),
		icons = c("cast", "insert_chart")
	)),
	
	material_side_nav_tab_content(
		side_nav_tab_id = "example_side_nav_tab_1",
		
		material_button(input_id = "button", label = "GO TO SECOND SIDE NAV TAB")
		
	),
	
	material_side_nav_tab_content(side_nav_tab_id = "example_side_nav_tab_2",
																
	  tags$h1("Second Side-Nav Tab Content"))
)

server <- function(input, output, session) {

	observeEvent(input$button, ignoreInit = T, {

		session$sendCustomMessage(type = "shinymaterialJS", js$select_material_Tab("example_side_nav_tab_2"))
		
	})
	
}

shinyApp(ui = ui, server = server)

JQUery/JS solution (basically just repeating the on click behaviour)

shinyjs.select_material_sidenav_tab = function(tab_id){
    $('.shiny-material-side-nav-tab-content').hide();
    $('.shiny-material-side-nav-tab-content').trigger('hide');
    $('.shiny-material-side-nav-tab-content').trigger('hidden');
    $('.shiny-material-side-nav-tab').removeClass('active');
    $('#' + tab_id).show();
    $('#' + tab_id).trigger('show');
    $('#' + tab_id).trigger('shown');
    $('#' + tab_id + '_tab_id').addClass('active');
    $('#side_nav_tabs_click_info').trigger('click');
}

Hi @analytichealth @Seyphaton , thank you for your previous responses to this issue. @Seyphaton any suggestion on how to programatically hide (and/or disable) a tab in material_tabs() given your JQUery/JS solution? I've been attempting to adapt your solution and only seem to be able to hide the content within the tab, not the tab itself.

Try this:

shinyjs.disableTab = function(name) {
    var tab = $('.nav li a[data-value=' + name + ']');
    tab.bind('click.tab', function(e) {
        e.preventDefault();
        return false;
    });
    tab.addClass('disabled');
};
shinyjs.hideTab = function(name) {
    var tab = $('.nav li a[data-value=' + name + ']');
    tab.bind('click.tab', function(e) {
        e.preventDefault();
         return false;
    });
    tab.addClass('invisible');
};