3 Replies Latest reply on Sep 9, 2018 6:56 PM by Man Tsui

    Filter synchronisation across embedded views

    timothy.charltonczaplicki

      Hi all,

       

      this might be a case of me barking up entirely the wrong tree but I though it was worth a try asking around here:

       

      I have a Tableau workbook published on our Tableau Server, composed of a 'results' sheet and a 'control' dashboard. The dashboard essentially only contains filters and a hidden 'results' sheet. What I am trying to do is embed two copies of the workbook in a page in our Drupal CMS using the JS Api, one displaying controls, one results. So far, so easy, embedding works fine and I have two elements containing two 'instances' of my workbook (viz and controlsViz). Now, selections in one instance are not reflected in the other. My attempt to solve this was to use the JS Api to access the filters from controlsViz, whenever changes are made and apply them to viz, essentially synchronising the two instances. The filters are all categorical. The below approach correctly reads the filters, their type and associated values. It then applies 'something' to 'viz', which goes blank. Also, this code only executes once, afterwards a complete page reload is necessary.

       

      controlsViz.getWorkbook().getActiveSheet().getWorksheets().get("results").getFiltersAsync().then(

                     function(filters) {

                     targetsheet = viz.getWorkbook().getActiveSheet().getWorksheets().get("results"); 

                     for (filter of filters) {

                       name = filter.getFieldName();  

                       type = filter.getFilterType();

                       values = [];

                       if ( type == 'categorical') {

                       try {

                         prepvalues = filter.getAppliedValues();

                           for (let i = 0; i < prepvalues.length; i++){

                             values.push(prepvalues[i].value);

                           }

                         try {

                           targetsheet.applyFilterAsync(name, values, tableau.FilterUpdateType.REPLACE).then(console.log('Filter '+name+' applied'));

                           } catch (ef) {

                             console.log(ef);

                         }

                       } catch (e) {

                         console.log(e);

                       }

                     }              

                    }

                  }

      )

       

      The reason I am doing this is to allow the design to scale responsively. I am at a loss at this point, if anyone can point out the flaw in this approach or (even better) suggest a simpler way of solving the problem I would greatly appreciate it.

       

      Many thanks in advance,

      Timothy

       

        • 1. Re: Filter synchronisation across embedded views
          Patrick A Van Der Hyde

          Timothy,

           

          This post was from a few weeks ago.  I am replying to bring the topic up again and to see if you have located a solution? 

           

          Patrick

          • 2. Re: Filter synchronisation across embedded views
            timothy.charltonczaplicki

            Hi Patrick,

             

            unfortunately not, for the meanwhile I am solving the problem by creating worksheets of varying sizes and content and switching between them as required. However, this is only a quick fix, my goal is still to achieve some kind of 'deeper' integration between multiple viz instances and website components.

             

            Thanks for bringing the topic back up,

            Timothy

            • 3. Re: Filter synchronisation across embedded views
              Man Tsui

              Hi Timothy,

               

              Very good question, I'll try to offer some direction and my experience.

              Scenario A) Are you trying to synchronize the filters when both dashboards were created?

              try wrapping when you're declaring your dashboard.

              onFirstInteractive: function () {  getFilterAsync() + applyFilterAsync()   }

              - For more examples, google "onFirstInteractive Tableau Javascript API"

              https://onlinehelp.tableau.com/current/api/js_api/en-us/JavaScriptAPI/js_api_concepts_initializing.htm

               

               

              Scenario B) Or are you trying to synchronize filters when the user change one filter on one of the dashboard? (filter change event.)

               

              My observation from your question, you haven't created an event listener for filter change.

               

              1) To my best knowledge, we like to do multiple dashboards all in one instance but that is not possible. Often times, multiple dashboards within a group of API-based dashboard calls will result in multiple instances. This is unavoidable.

               

              2) Part of planning is where are you writing the javascript api code, within index.html or within one javascript file.

               

              Scenario B

              I have chosen to go to the route of writing everything in one javascript file.

              I would:

                 Step 1) Create event listener for filter change in the source dashboard

                 Step 2) Wrap a getFilterAsync() to get the new categorical filters from source dashboard

                 Step 3) Additionally wrap applyFilterAsync() to push the new categorical filters to the target dashboard(s)

               

              viz01_Source.addEventListener('filterchange', function(filterEvent) {

               

              var arrayFilterList = [];

              filterEvent.getFilterAsync().then( function(field){

              var field_name = field.getFieldName();

              var field_type = field.getFilterType();

              if (field_name == "State") {

              var data_values = field.getAppliedValues();

              for (i = 0; i < data_values.length; i++) {

              var selectedFilterSingle = data_values[i].value;

               

              // Array manipulation: Concatenate multiple filter values into the array

              arrayFilterList.push(selectedFilterSingle);

              }

               

              // Cross-filter: Apply "State" filter criteria to "Target dashboard 02"

              setFilterTo(viz02_Target, 'OP Map', 'Provider State', arrayFilterList);

              // Cross-filter: Apply "State" filter criteria to "Target dashboard 03"

              setFilterTo(viz03_Target, 'IP Map', 'Provider State', arrayFilterList);

              }

              });

              });

               

              // Filter the specified dimension to the specified value(s)

              function setFilterTo(vizName, sheetName, filterName, values) {

              var sheet = vizName.getWorkbook().getActiveSheet().getWorksheets().get(sheetName);

                  sheet.applyFilterAsync(filterName, values, tableau.FilterUpdateType.REPLACE);

              }

               

               

              Live sample:

              https://mantsui.github.io/Tableau_Web_Portal01/

              This is how I synchronize categorical filter (State) from "Medicare Inpatient Charge Analysis dashboard"  with the "Medicare Outpatient Charge Analysis dashboard".

               

              Sample javscript file:

              https://github.com/mantsui/Tableau_Web_Portal01/blob/gh-pages/Tableau_JS_Embed.js