Skip to main content

I thought I was cool and had figured something out here. but alas. I’m humbled.


The goal is to create a next tab and previous tab script that can handle tabs that can be conditionally rendered away.Generally, the expected syntax for ‘next tab’ would run something like this:

tabset.tabs(“option”, “active”, tabset.tabs(“option”, “active”) +1). But that causes trouble when a tab is conditionally rendered away.


Here’s a page to see what’s happening:


<skuidpage unsavedchangeswarning="" showsidebar="false" showheader="true">   <models>
<model id="User" limit="0" query="false" createrowifnonefound="true" sobject="User" doclone="" type="">
<fields>
<field id="IsActive"/>
<field id="UserPreferencesActivityRemindersPopup"/>
<field id="ReceivesAdminInfoEmails"/>
<field id="ForecastEnabled"/>
<field id="UserPermissionsMobileUser"/>
<field id="UserPreferencesApexPagesDeveloperMode"/>
<field id="UserPermissionsCallCenterAutoLogin"/>
<field id="EmailPreferencesAutoBcc"/>
<field id="EmailPreferencesAutoBccStayInTouch"/>
<field id="UserPermissionsChatterAnswersUser"/>
<field id="UserPreferencesContentEmailAsAndWhen"/>
<field id="UserPreferencesContentNoEmail"/>
</fields>
<conditions/>
<actions/>
</model>
</models>
<components>
<basicfieldeditor showheader="true" showsavecancel="false" model="User" buttonposition="" mode="edit" layout="">
<columns>
<column width="33.3%">
<sections>
<section title="Section A" collapsible="no" showheader="false">
<fields>
<field id="IsActive" valuehalign="" type="">
<label>A</label>
</field>
<field id="UserPreferencesActivityRemindersPopup" valuehalign="" type="">
<label>B</label>
</field>
<field id="ReceivesAdminInfoEmails" valuehalign="" type="">
<label>C</label>
</field>
<field id="ForecastEnabled" valuehalign="" type="">
<label>D</label>
</field>
</fields>
</section>
</sections>
</column>
<column width="33.3%">
<sections>
<section title="Section B" collapsible="no" showheader="false">
<fields>
<field id="UserPermissionsMobileUser" valuehalign="" type="">
<label>E</label>
</field>
<field id="UserPreferencesApexPagesDeveloperMode" valuehalign="" type="">
<label>F</label>
</field>
<field id="UserPermissionsCallCenterAutoLogin" valuehalign="" type="">
<label>G</label>
</field>
<field id="EmailPreferencesAutoBcc" valuehalign="" type="">
<label>H</label>
</field>
</fields>
</section>
</sections>
</column>
<column width="33.3%">
<sections>
<section title="Section C" collapsible="no" showheader="false">
<fields>
<field id="EmailPreferencesAutoBccStayInTouch" valuehalign="" type="">
<label>I</label>
</field>
<field id="UserPermissionsChatterAnswersUser" valuehalign="" type="">
<label>J</label>
</field>
<field id="UserPreferencesContentEmailAsAndWhen" valuehalign="" type="">
<label>K</label>
</field>
<field id="UserPreferencesContentNoEmail" valuehalign="" type="">
<label>L</label>
</field>
</fields>
</section>
</sections>
</column>
</columns>
</basicfieldeditor>
<tabset rememberlastusertab="false" defertabrendering="false" renderas="">
<tabs>
<tab name="A">
<components/>
<renderconditions logictype="and">
<rendercondition type="fieldvalue" operator="=" enclosevalueinquotes="false" fieldmodel="User" sourcetype="fieldvalue" nosourcerowbehavior="deactivate" field="IsActive" value="true"/>
</renderconditions>
</tab>
<tab name="B" loadlazypanels="true">
<components/>
<renderconditions logictype="and">
<rendercondition type="fieldvalue" operator="=" enclosevalueinquotes="false" fieldmodel="User" sourcetype="fieldvalue" nosourcerowbehavior="deactivate" field="UserPreferencesActivityRemindersPopup" value="true"/>
</renderconditions>
</tab>
<tab name="C" loadlazypanels="true">
<components/>
<renderconditions logictype="and">
<rendercondition type="fieldvalue" operator="=" enclosevalueinquotes="false" fieldmodel="User" sourcetype="fieldvalue" nosourcerowbehavior="deactivate" field="ReceivesAdminInfoEmails" value="true"/>
</renderconditions>
</tab>
<tab name="D" loadlazypanels="true">
<components/>
<renderconditions logictype="and">
<rendercondition type="fieldvalue" operator="=" enclosevalueinquotes="false" fieldmodel="User" sourcetype="fieldvalue" nosourcerowbehavior="deactivate" field="ForecastEnabled" value="true"/>
</renderconditions>
</tab>
<tab name="E" loadlazypanels="true">
<components/>
<renderconditions logictype="and">
<rendercondition type="fieldvalue" operator="=" enclosevalueinquotes="false" fieldmodel="User" sourcetype="fieldvalue" nosourcerowbehavior="deactivate" field="UserPermissionsMobileUser" value="true"/>
</renderconditions>
</tab>
<tab name="F" loadlazypanels="true">
<components/>
<renderconditions logictype="and">
<rendercondition type="fieldvalue" operator="=" enclosevalueinquotes="false" fieldmodel="User" sourcetype="fieldvalue" nosourcerowbehavior="deactivate" field="UserPreferencesApexPagesDeveloperMode" value="true"/>
</renderconditions>
</tab>
<tab name="G" loadlazypanels="true">
<components/>
<renderconditions logictype="and">
<rendercondition type="fieldvalue" operator="=" enclosevalueinquotes="false" fieldmodel="User" sourcetype="fieldvalue" nosourcerowbehavior="deactivate" field="UserPermissionsCallCenterAutoLogin" value="true"/>
</renderconditions>
</tab>
<tab name="H" loadlazypanels="true">
<components/>
<renderconditions logictype="and">
<rendercondition type="fieldvalue" operator="=" enclosevalueinquotes="false" fieldmodel="User" sourcetype="fieldvalue" nosourcerowbehavior="deactivate" field="EmailPreferencesAutoBcc" value="true"/>
</renderconditions>
</tab>
<tab name="I" loadlazypanels="true">
<components/>
<renderconditions logictype="and">
<rendercondition type="fieldvalue" operator="=" enclosevalueinquotes="false" fieldmodel="User" sourcetype="fieldvalue" nosourcerowbehavior="deactivate" field="EmailPreferencesAutoBccStayInTouch" value="true"/>
</renderconditions>
</tab>
<tab name="J" loadlazypanels="true">
<components/>
<renderconditions logictype="and">
<rendercondition type="fieldvalue" operator="=" enclosevalueinquotes="false" fieldmodel="User" sourcetype="fieldvalue" nosourcerowbehavior="deactivate" field="UserPermissionsChatterAnswersUser" value="true"/>
</renderconditions>
</tab>
<tab name="K" loadlazypanels="true">
<components/>
<renderconditions logictype="and">
<rendercondition type="fieldvalue" operator="=" enclosevalueinquotes="false" fieldmodel="User" sourcetype="fieldvalue" nosourcerowbehavior="deactivate" field="UserPreferencesContentEmailAsAndWhen" value="true"/>
</renderconditions>
</tab>
<tab name="L" loadlazypanels="true">
<components/>
<renderconditions logictype="and">
<rendercondition type="fieldvalue" operator="=" enclosevalueinquotes="false" fieldmodel="User" sourcetype="fieldvalue" nosourcerowbehavior="deactivate" field="UserPreferencesContentNoEmail" value="true"/>
</renderconditions>
</tab>
</tabs>
</tabset>
</components>
<resources>
<labels/>
<javascript/>
<css/>
</resources>
</skuidpage>

To reproduce the issue:



  1. Check C, D, E, F, G, I, and J.




  2. Open Console



//define these variables:
var $ = skuid.$;
var tabset = $('body').find('.ui-tabs').first();
var initialTabNumberArray = r],
secondTabNumberArray = ],
thirdTabNumberArray = ],
fourthTabNumerArray = ],
fifthTabNumberArray = ];

  1. Get the tab number values from jQuery ui tabs widget

//Click on each tab and run
initialTabNumberArray.push(tabset.tabs("option", "active"));

As a result, initialTabNumberArray should be

r0, 1, 2, 3, 4, 5, 6], as expected.


  1. Uncheck E, G, and I (leaving only C, D, F, and J)

//Click on each tab and run
secondTabNumberArray.push(tabset.tabs("option", "active"));

As a result, secondTabNumberArray is

u0, 1, 3, 6].


This is no problem, as long as it’s consistent.


We should be able to do something like

var tabpanels = $(tabset).children(‘.ui-tabs-panel’), and loop through the tabpanels after the active tab until we find one that has the attribute data-rendered=“true”, and pass that value back into tabToActivate in tabset.tabs(“option”, “active”, tabToActivate).


  1. Now check A E H K L, so your current visible tabs are A C D E F H J K L

//Click on each tab and run
thirdTabNumberArray.push(tabset.tabs("option", "active"));

thirdTabNumberArray returns <0, 1, 2, 3, 4, 5, 6, 7, 8]

Tabs have reset to have 0-8 sequentially.



  1. Check I, uncheck J K E so your current visible tabs are A C D F H I L, and populate fourthTabNumberArray, which returns p0, 1, 2, 4, 5, 6, 9]




  2. Uncheck A and L so your current visible tabs are C D F H I J, and populate fifthTabNumberArray, which returns p0, 1, 2, 3, 4, 5].



In the third and fifth tab number arrays, i can no longer use the tabpanels jQuery method described above to check if the next tab is rendered, because as soon as the ui-tabs widget resets the indexes of the tabs, I have no way to reliably connect those indexes to those of the tabpanels array created with $(tabset).children(‘.ui-tabs-panel’). I either need a reliable way to connect the indexes, or some other way to determine if the ‘next panel’ is rendered. Ideally, i could force the ui-tabs widget to reset the indexes of the tabs every time there was a change in rendering, so I could just run tabset.tabs(“option”, “active”, tabset.tabs(“option”, “active”) +1) and not have to check for visiblity.


Thoughts?

I know this is a complex one, but I’m committed to making this work and keep hitting my head on the wall.

^^bump^^


^^^^^^


Oh wow Matt. Most of the support team was out of the office last week.  This item is pretty heavy to pick back up.  I promise though I’ll take a look at it later this afternoon. 


No worries. And thanks… You all are awesome!


Rob, et al.,

Has anyone been able to take a look at this? It would be great to have some kind of solution (or at least a pointer in the right direction) today before we get into the long weekend.


Hey Matt, unfortunately I don’t think I’ll have the time to give you a complete solution today before the long weekend. However, it seems your troubles are stemming from an unreliable way to access the next tab . You could try giving them all your tabs unique data attribute on pageload with something like this.


var $ = skuid&#46;$;<br />$(function(){<br />&nbsp; &nbsp; $('#myTabs')&#46;children()&#46;each(function(i, el){<br />&nbsp; &nbsp; &nbsp; &nbsp; var $el = $(el);<br />&nbsp; &nbsp; &nbsp; &nbsp; $el&#46;attr('tab-index', i);<br />&nbsp; &nbsp; });<br />});


Of course this is assuming you gave your tabset the id “myTabs”. You then could access each individual tab, via a jQuery selector similar to this one.


$('#myTabs divTtab-index="4"') 

Andrew, I don’t have a problem accessing the next tab with jQuery. I don’t know how to activate a tab that I’ve selected with jQuery. I can’t seem to find a way to reliably use the .tabs(“option”,“active”) method. How can I activate a specific jQuery selected tab?


How about something like this?


function activateTab($tabset, $tab){ $tab = $(tab); console.log($tabset, $tab); var children = $tabset.children(); var visibleChildrenCount = 0; $.each($tabset.find('li'), function(i, el){ console.log(el); $el = $(el); if($el.is($tab)){ console.log('yay', visibleChildrenCount); $tabset.tabs('option', 'active', visibleChildrenCount); return; } if($el.css('display') !== 'none'){ console.log(visibleChildrenCount); visibleChildrenCount++; } }); }


And then you could activate a specific tab (if it’s visible) with something like this.


activateTab($('#myTabs'), $('#myTabs lildata-tab="skuid-component-tab-24"]'))

Andrew, Thanks for the reply. That was basically what I attempted, but it doesn’t work in the case where the user conditionally renders a tab away. For example, if I have four tabs, A,B,C,D, all visible. The tabs(“option”,“active”) indexes are 0,1,2,3. If the user hides tab C, the indexes are 0,1,3. If I’m reading your code correctly, it would try to activate tab 2, which isn’t visible. Am I reading that wrong? The troublesome issue is that if the user then hid tab A, and then rendered A again, the tab indexes reset, and for A,B,D would be 0,1,2. Is that clearer?


Yes, that is clearer, and I think I found the source of your troubles. This piece of code.


// If we are not doing our initial load, // refresh the jQuery UI tab nature of our DOM elements if (!component.isInitiallyLoading) { component.element.tabs('refresh'); }


As you can tell, the tabset is being refreshed only when the tab has been rendered before. This is the sort of behavior you are referencing. Knowing that, the approach I would take would be something like this. 


$('inputttype=checkbox]').click(function(){ $('#myTabs').tabs('refresh'); });


There’s probably a more elegant way to do this, but refreshing the tabset every time a new tab is rendered or unrendered ensures that the indexing of the tabs matches up with what is being displayed. From there, you can more reliably figure out the index of any given tab.


Thank you, thank you, thank you! Exactly what I needed.

It’s probably overkill, but I’m just calling tabs(‘refresh’) every time I try to change tabs.


I’m trying to use this code and am gettin an “Uncaught ReferenceError: tab is not defined”

I’ve set the unique ID of my tabset “reportMainTabset”, and set the “Tab panel Unique Id” to t3,  and I’m using the function:


function activateTab($tabset, $tab){


$tab = $(tab);<br />console&#46;log($tabset, $tab);<br />var children = $tabset&#46;children();<br />var visibleChildrenCount = 0;<br />$&#46;each($tabset&#46;find('li'), function(i, el){<br />console&#46;log(el);<br />$el = $(el);<br />if($el&#46;is($tab)){<br />console&#46;log('yay', visibleChildrenCount);<br />$tabset&#46;tabs('option', 'active', visibleChildrenCount);<br />return;<br />}<br />if($el&#46;css('display') !== 'none'){<br />console&#46;log(visibleChildrenCount);<br />visibleChildrenCount++;<br />}<br />});<br />}


And calling like so:



activateTab($(‘#reportMainTabset’), $(‘#reportMainTabset liedata-tab=“t3”]’));


What’s wrong here?


Reply