Multiple Layer Toggle Button instances getting out of sync

1354
8
Jump to solution
02-03-2020 03:41 PM
EricRuberson1
New Contributor III

I'm using 7 instances of LayerToggleButton (7 separate config files, 1 widget folder).

Our WAB opens with some layers already displaying on the map. The first toggle button says 'off', the rest say 'on' if you hover over them.

Our first Toggle Button is the default layers. Push it once, all the layers that were on, get turned off. The layers are now off, the hover text on the icon switched to 'on', all the rest of the Toggle Buttons still say 'On' if hovered over.

You can cycle that same button repeatedly and those cycle like that.

But say I click the second Toggle Button? If the layers from the first button were showing, pushing the second button flips to it's layers (good), push it again, its still properly flipping the layers on and off.

But if I start bouncing between these buttons, layers start staying on and off irrespective of whatever they were supposed to be. You can sync them back up again by pressing the same button repeatedly. But they start acting wierd as you go between them. I swear I had at one point a cycle of three button presses: the button's assigned layers, the default layers, and no layers showing, but I think that only happens after you've switched between buttons.

I also added some (hacky, non widget oriented) code that syncs the grouped layers on/off checkboxes, but they arent properly in sync with the layers either. Usually they are the opposite of what the layer is displaying (i.e. the layers arent actually visible but the checkboxes are on, hit the toggle button, and those all flip). That code works, but I will probably need to post it on here as a seperate question because it needs to be converted to proper widget oriented syntax (rather than decending up and down an arbitrary set of dom elements). It's just doing like the exact opposite of what it's supposed to be.

I feel like this is a simple logic issue but I'm not sure where that issue lies.

Tags (1)
0 Kudos
1 Solution

Accepted Solutions
RobertScheitlin__GISP
MVP Emeritus

Eric,

  OK you are using a custom TOC widget that does grouping. I am not sure how that is affecting the layer toggle button widget.

The other thing that is likely affecting you in your use case is that you are using exclusive mode for each of the toggles. The logic in my widget is this:

      toggleLayer: function(lObjs) {
        var bmLyrObjs = this.layerStructure.getBasemapLayerObjects();
        var bmlyrIds = [];
        array.map(bmLyrObjs, lang.hitch(this, function(bmObj, index) {
          bmlyrIds.push(bmObj.id);
        }));
        if (!lObjs[0].isToggledOn() && this.isExclusive) {
          this.layerStructure.traversal(lang.hitch(this, function(layerNode) {
            if(bmlyrIds && bmlyrIds.indexOf(layerNode.id)<0 ){ //&& this.toggleLayerIds.indexOf(layerNode.id)<0
              layerNode.hide();
            }
          }));
        }
...
//Then toggle the layers configured in the widget‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

Line 7 means that if the first configured layer in the widgets list is NOT visible (i.e. toggled on) and the mode is exclusive, then I am assuming that the layers that the button are configured for are OFF and they need to be toggled on. So i turn all layers in the map OFF and then just toggle all configured layers. This is where I think my logic is flawed in my widget because I do not consider the situation where a layer could be configured to be off and I don't consider that the first configured layer for the widget could be ON by default.

The other thing o consider is that this widget is called the toggle layer button widget because that is exactly what it does. I you click one button and it toggles a particular layer on and then you click a second button that is configured to have that layer on also when you click that button if will turn that layer off (by design) because it is already on and to toggle that layer means to turn it off.

Needless to say I need to go back to the drawing board on this widget and you should not spend any more time trying to get this to work for your site.

But if your desire is to have the widget not act like a toggle and just turn ON layers that are configured (meaning that the second click would only try to turn ON the same configured layers) then you can make a couple quick changes to my code to make that happen.

      toggleLayer: function(lObjs) {
        var bmLyrObjs = this.layerStructure.getBasemapLayerObjects();
        var bmlyrIds = [];
        array.map(bmLyrObjs, lang.hitch(this, function(bmObj, index) {
          bmlyrIds.push(bmObj.id);
        }));
        if (this.isExclusive) {
          this.layerStructure.traversal(lang.hitch(this, function(layerNode) {
            if(bmlyrIds && bmlyrIds.indexOf(layerNode.id)<0 ){
              layerNode.hide();
            }
          }));
        }
...‍‍‍‍‍‍‍‍‍‍‍‍‍‍

Line 7 I remove the "!lObjs[0].isToggledOn() && "

The in the apps configs/LayerToggleButton/config_widgets_LayerToggleButton_Widget_xx.json

delete all the items that are "display": false. i.e

    "FFO_MRCOG_Location_Reference_Systems_Web_2019_12_30_CJM_8784": {
      "display": false
    },

Those changes will make the button turn all layers in the map off and turn on the configured layers.

View solution in original post

8 Replies
RobertScheitlin__GISP
MVP Emeritus

Eric,

   I you could provide a app link that I could access then I would be willing to look into your issue. As of now you are the only person reporting issues like this so I am not sure what the issue is in your case.

0 Kudos
EricRuberson1
New Contributor III

Robert,

I'm going to have to edit the URL out later... particularly since its supposed to end up password protected eventually.

[url deleted]

This is the live version of our site.

Click through the splash screen and check out the hot buttons. I'm sure there are other issues. I know that I'm not properly closing one of the widgets because I have something in the menu code triggering something to do with it. I think that's the Add Data thing.

0 Kudos
RobertScheitlin__GISP
MVP Emeritus

Eric,

  I am sorry but as that site has errors that are not related to my widget. I can not invest time to debug issues that are not related to my widgets. If you can produce as test site that is using my widget and has no errors that are not related to my widget then I will take a look.

0 Kudos
EricRuberson1
New Contributor III

Understood.

0 Kudos
EricRuberson1
New Contributor III

Robert,

Please check out [Link Deleted: Robert answered the question and it is no longer needed!: 2020/02/10 by post author]

We've stripped out just about every other widget that was in it. What remains is the 7 layer toggle button icons and the layer list. The app has been ported to 2.15 to rule out versioning issues.

Example of problem workflow:

0) Once the page loads, it shows the default layers we want on at the start of the map, and they should correspond to the layers associated with the second widget icon (shaped like a crosshair)

1) Click the wind industry icon in the upper right (the wind turbine). A bunch of layers turn off (some that should actually be on) and it mainly shows the green wind development resources grid.

2) Click it again and it shows the grid plus the bulk of the other layers that were on. I think this is what the button is actually supposed to toggle on.

3) One more time and they all turn off.

4+)Now if you click it repeatedly, that button behaves the way it is supposed to, all assigned layers off or on.

Continuing from there:

Click through the layer toggle buttons until all the layers are off.

Now click the button to the right of the wind turbine once (hover text calls it counties)

Then click the button to the right of that once (hover text should say cities and towns). You should get remnant layers that for some reason dont get turned off.

So these are examples of our syncing issue. If you click around the layer toggle buttons, layers appear to intermittently linger or only partially get turned on like they are supposed to.

2nd issue we have:

Getting the group checkmarks in the layer list to turn off and on when the layers themselves are toggled on and off.

I have come up with some code for that which does work (though it's doing it opposite of what the actual layer visibility shows, which, if it persists after fixing the syncing issue, should just be a matter swapping the check/uncheck lines).

The main problem with my code is that it's not written using the 'node' style that I think these widgets are supposed to be written in, it's using a hard coded path up and down the node trees to work. I basically dont understand the widget/node structure enough to make this not look like sloppy code. The lines I'm refering to mainly are lines 38, 39, and 51.

I'm including it, below. If you would rather I broke it out to a second thread, I would be happy to. This code is not incorporated into the code of the website linked to above, in order to keep that page as code-original as possible.

This would go into LayerToggleButton's widget.js and replace the toggleLayer function

     toggleLayer: function(lObjs) {
        var bmLyrObjs = this.layerStructure.getBasemapLayerObjects();
        var bmlyrIds = [];
        array.map(bmLyrObjs, lang.hitch(this, function(bmObj, index) {
          bmlyrIds.push(bmObj.id);
        }));
        if (!lObjs[0].isToggledOn() && this.isExclusive) {
          this.layerStructure.traversal(lang.hitch(this, function(layerNode) {
            if(bmlyrIds && bmlyrIds.indexOf(layerNode.id)<0){
              layerNode.hide();
            }
          }));
        }

        
        var onOff;
        array.map(lObjs, lang.hitch(this, function(lObj, index) {
          onOff = (lObj.isToggledOn()) ? 'On' : 'Off';
          if (lObj._layerInfo && lObj._layerInfo.layerObject) {
            this.setBusyIndicator(lObj);
          }

          if (this.parentContainer && this.originalTitle) {
            html.setAttr(this.parentContainer, 'title', this.originalTitle + ': ' + onOff);
          }
          if (onOff === 'Off' && index === 0 && lObj._layerInfo && lObj._layerInfo.layerObject && lObj._layerInfo.layerObject.fullExtent) {
            if (this.zoomToLayer) {
              this.map.setExtent(lObj._layerInfo.layerObject.fullExtent.expand(1.2));
            }
          }
          lObj.toggle();

          var aSearchId = lObj._layerInfo.layerObject.id;

          var objectsElement = query("[class~='layer-tr-node-"  + aSearchId + "']")[0];

          if (objectsElement!=undefined){
            var targetElement = query(objectsElement).parent().prev(); //the ancestor of the group's checkbox element
            var grpCheckbox = targetElement[0].childNodes[0].childNodes[0].childNodes[1].childNodes[0]; //the checkbox for the group
            var visibleCheckBox = registry.byNode(grpCheckbox); //used for setting the checkbox later
            
            if(onOff=== 'On') { //if true, layer on, we need group checkbox on
              visibleCheckBox.check();
            } else { // before we can turn off the checkbox, we need to search to see if any of the other layers in the group are supposed to be on

              var targetSiblings = query(objectsElement).siblings();
              var groupOnOff = false;
              for (var lyr in targetSiblings){//loop through all siblings in the group, checking for checkbox status
                var checkStatus = "no check";
                try{
                  checkStatus = targetSiblings[lyr].childNodes[0].childNodes[1].childNodes[0].getAttribute('aria-checked');
                }
                catch(error){ //checkStatus line throws an error if .childNodes is used on a non-existant element. If we don't catch the error, the page will break.
                  checkStatus = "no check";
                }
                if (checkStatus !== "no check"){ 
                  if(!groupOnOff && checkStatus ==="true"){
                    groupOnOff = true;
                  }
                }
              }   
              if(!groupOnOff) { //if none of the siblings are checked, uncheck the parent
                visibleCheckBox.uncheck();
              }
            } 
          }

          
          topic.publish('toggleChanged', lObj._layerInfo._visible, lObj);
          if (!lObj.isVisible() && lObj.isToggledOn()) {
            lObj.show();
          }
        }));
      }‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
0 Kudos
RobertScheitlin__GISP
MVP Emeritus

Eric,

  OK you are using a custom TOC widget that does grouping. I am not sure how that is affecting the layer toggle button widget.

The other thing that is likely affecting you in your use case is that you are using exclusive mode for each of the toggles. The logic in my widget is this:

      toggleLayer: function(lObjs) {
        var bmLyrObjs = this.layerStructure.getBasemapLayerObjects();
        var bmlyrIds = [];
        array.map(bmLyrObjs, lang.hitch(this, function(bmObj, index) {
          bmlyrIds.push(bmObj.id);
        }));
        if (!lObjs[0].isToggledOn() && this.isExclusive) {
          this.layerStructure.traversal(lang.hitch(this, function(layerNode) {
            if(bmlyrIds && bmlyrIds.indexOf(layerNode.id)<0 ){ //&& this.toggleLayerIds.indexOf(layerNode.id)<0
              layerNode.hide();
            }
          }));
        }
...
//Then toggle the layers configured in the widget‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

Line 7 means that if the first configured layer in the widgets list is NOT visible (i.e. toggled on) and the mode is exclusive, then I am assuming that the layers that the button are configured for are OFF and they need to be toggled on. So i turn all layers in the map OFF and then just toggle all configured layers. This is where I think my logic is flawed in my widget because I do not consider the situation where a layer could be configured to be off and I don't consider that the first configured layer for the widget could be ON by default.

The other thing o consider is that this widget is called the toggle layer button widget because that is exactly what it does. I you click one button and it toggles a particular layer on and then you click a second button that is configured to have that layer on also when you click that button if will turn that layer off (by design) because it is already on and to toggle that layer means to turn it off.

Needless to say I need to go back to the drawing board on this widget and you should not spend any more time trying to get this to work for your site.

But if your desire is to have the widget not act like a toggle and just turn ON layers that are configured (meaning that the second click would only try to turn ON the same configured layers) then you can make a couple quick changes to my code to make that happen.

      toggleLayer: function(lObjs) {
        var bmLyrObjs = this.layerStructure.getBasemapLayerObjects();
        var bmlyrIds = [];
        array.map(bmLyrObjs, lang.hitch(this, function(bmObj, index) {
          bmlyrIds.push(bmObj.id);
        }));
        if (this.isExclusive) {
          this.layerStructure.traversal(lang.hitch(this, function(layerNode) {
            if(bmlyrIds && bmlyrIds.indexOf(layerNode.id)<0 ){
              layerNode.hide();
            }
          }));
        }
...‍‍‍‍‍‍‍‍‍‍‍‍‍‍

Line 7 I remove the "!lObjs[0].isToggledOn() && "

The in the apps configs/LayerToggleButton/config_widgets_LayerToggleButton_Widget_xx.json

delete all the items that are "display": false. i.e

    "FFO_MRCOG_Location_Reference_Systems_Web_2019_12_30_CJM_8784": {
      "display": false
    },

Those changes will make the button turn all layers in the map off and turn on the configured layers.

EricRuberson1
New Contributor III

Robert, thank you so much! That was exactly what we were looking for.

You are correct in that we weren't looking for toggles so much as preset lists of layers to set the map to. And that change worked like a charm.

Deleting the the URL from the above message. My boss will probably pull it down by the end of the day.

0 Kudos
LSAAssociates
New Contributor II

Could you integrate an "ignore" option in the config json file? So you could have, "display": true, false, or ignore. Even with the fix that you provided, some of the layers are still getting out of "sync" (full knowing that this is using the toggle widget as a turn on or off layers widget). 

0 Kudos