Popup Legend similar to Nliu's

3266
11
07-22-2013 08:31 AM
RyanWhiley
New Contributor
Hello everyone,

Right now I am currently working on trying to get a popup legend on the side of my map very similar to Nliu's" rel="nofollow" target="_blank">http://maps.charlottenc.gov/templates/basicviewer/?webmap=460... Example where you would click a button in the toolbar and the left panel would pop out with the information you want.  I looked through the code to see what they were using to make that work and I found no leads.  I tried to use a floatingPane but that did not work how I wanted it to.  So I was wondering if anyone could give me an idea of what I would use to start this process.


Thanks a lot!
Ryan
0 Kudos
11 Replies
DianaBenedict
Occasional Contributor III
I have been able to use Nliu's legend object and added it to a dijit/for/DropDownButton using the following HTML:

[HTML]
<div id="baseLayersDropDown" class="baseLayersDiv" data-dojo-type="dijit/form/DropDownButton" data-dojo-props="iconClass:'baseMapIcon',label:'Base Layers', showLabel: true, disabled: false">
<div data-dojo-type="dijit/TooltipDialog" class="tooltipDialogLayers">
<div id="divBaseLegendHolder" ></div>
</div>
</div>

/* the css for the baselayersDiv is as follow: */
.baseLayersDiv
{
    top:5px;
    right:10px;
    position:absolute;
    cursor: pointer;
    z-index:100;
    padding-right: 30px;
    opacity:0.90;
    filter:alpha(opacity=90);
    padding: 0px;
    vertical-align:top;
    font-style:normal;
    font-size: 10px;
    float: right;


/* baseMap Icon */
.baseMapIcon
{
    background-image:url('../images/Basemap.png');
    width:16px;
    height:16px;
    font-size:6pt;
    font-weight: bold;
}
[/HTML]

Note that I added an IconClass so that I can have an Icon Displayed along with the text label .. your choice on UI.

In your JS code when you are instantiating the new agsjs.dijit.TOC object, pass in the name of the "divBaseLegendHolder" as the div to attach the legend content to.


Hope it works for you
0 Kudos
RyanWhiley
New Contributor
Hi Diana, thanks for the reply.  I was able to implement some of that code into one of my existing drop down menus to improve it.  But what I am trying to do with the legend is when you select the legend button, the left panel expands open with the legend contained inside it.  And then I would like to have another button for my editing tools, where the tools would also appear in the left panel when the button is clicked.  I don't think that will be very hard to do though once I figure out how to go about making the left panel expand when a button is selected.  If there's any way you could assist me in getting started with this, I would really appreciate it.


Thanks,
Ryan
0 Kudos
DianaBenedict
Occasional Contributor III
Hi Diana, thanks for the reply.  I was able to implement some of that code into one of my existing drop down menus to improve it.  But what I am trying to do with the legend is when you select the legend button, the left panel expands open with the legend contained inside it.  And then I would like to have another button for my editing tools, where the tools would also appear in the left panel when the button is clicked.  I don't think that will be very hard to do though once I figure out how to go about making the left panel expand when a button is selected.  If there's any way you could assist me in getting started with this, I would really appreciate it.


Thanks,
Ryan


Ryan

I guess it depends on what type of dijits or dom elements you are using for your left panel.  The way I see it is that you will need to connect an onClick event on dijit/form/DropDownButton. You can either do this programatically or in line in the html. The onClick event should call a function that will set the domStyle display to block if you want it to be visible (ex. domStyle.set(leftDiv.domNode, 'display', 'block');) or 'none' if want to hide it - unless you are using some other UI element that has an onHide event of some sort. You can organize your left panel with  dijit/TitlePane to seperate the Legend from the editor. This tends to work pretty well and the UI looks really nice and clean. Below is an example of how I connect the "toggle" event for my paneEditor titlePane dijit.

 var titlePane = dijit.byId("paneEditor");
 dojo.connect(titlePane, "toggle", function () {
   if (titlePane.open && _mapController.GetEnableEditing()) {
     if (_map.getScale() < _mapController.minEditScale) {
        if (!_mapController.isTemplatePickerInitialized) {
         initializeEditorTemplate();
         titlePane.resize();
       }
       if (!_mapController.isAttributeInspectorInitialized) {
         initializeAttributeInspector();
       }
     }
   }
 });
0 Kudos
RyanWhiley
New Contributor
Diana,

Thanks for your assistance.  But I am still a little confused.  So I would attach an onClick event to the DropDownButton and call a function like so
 <div id="baseLayersDropDown" class="baseLayersDiv" onClick="leftPanel1();" data-dojo-type="dijit/form/DropDownButton" data-dojo-props="iconClass:'',label:'Legend', showLabel: true, disabled: false">
<div data-dojo-type="dijit/TooltipDialog" class="tooltipDialogLayers">
<div id="divBaseLegendHolder" ></div>
</div>
</div>



Then in my JS I would have the leftPanel1 function with the code you supplied like this?
function leftPanel1 () {
domStyle.set(leftDiv.domNode, 'display', 'block');
}


And would the other code you supplied (var titlePane = dijit.byId("paneEditor");...) go into that same function?
I also have to add a div id into the left panel in my HTML so that the proper information gets pulled up there right?
0 Kudos
DianaBenedict
Occasional Contributor III
Below is the html. Note that I was able to utilize some of the code that is already supplied in an excellent example of a base map template that was created and published by David Spriggs here:
https://github.com/DavidSpriggs/ConfigurableViewerJSAPI

The HTML below has the following:
1) sidebarCollapseButton div to handle the display style of the left panel
2) left panel div with two title panes and divs to hold the legend and editor tools

[HTML]
<div id="sidebarCollapseButton" class="sidebarCollapseButton close" style="display:none;"></div>

<div id="leftDiv"  data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region:'left'" style="display:block;">  
  <div id="paneLayers"  class="titlePane" data-dojo-type="dijit/TitlePane" data-dojo-props="title: 'Layers', open: true" >
    <div id="divLegendHolder"></div>
  </div>
  <div id="paneEditor"  class="titlePane" data-dojo-type="dijit/TitlePane" data-dojo-props="title: 'Editor Tools', open: false" >
        <div id="editToolbarHolder" ></div>
        <div id="editTemplateHolder" ></div>
  </div>
</div>

/* CSS needed for the sidebarCollapse */
.sidebarCollapseButton {
    width: 20px;
    height: 25px;
    position: absolute;
    top: 220px;
    border-top: 1px solid #B5BCC7;
    border-right: 1px solid #B5BCC7;
    border-bottom: 1px solid #B5BCC7;
    z-index: 40;
    background-color: white;
    border-radius: 0 5px 5px 0;
    background-position: 2px 4px;
    background-repeat: no-repeat;
    cursor: pointer;
}
.sidebarCollapseButton.close {
    left: 280px;
    background-image: url("../images/close.png");
}
.sidebarCollapseButton.open {
    background-image: url("../images/open.png");
    left: 0;
}

[/HTML]

Within the map Init function I call a method similar to the one that David Spriggs has that sets up the sidebarCollapse click event and sets the display style to block.  Note that since I have not been able to ugrade my app to AMD I had to get a little "creative". The easiest way I was able to manage the style and get access to the dom objects was by requiring the following dojo libs:
"dojo/dom", "dojo/dom-class", "dojo/on", "dojo/dom-style"

require(["dojo/dom", "dojo/dom-class", "dojo/on", "dojo/dom-style"],
    function (dom, domClass, dojoOn, domStyle) {
      var sidebarCollapseDiv = dom.byId('sidebarCollapseButton');
      var leftDiv = dijit.byId("leftDiv");
      dojoOn(sidebarCollapseDiv, 'click', function (evt) {
        if (leftDiv.domNode.style.display === "none") {
          //domStyle.set(sidebarCollapseDiv, 'display', 'block');
          domStyle.set(leftDiv.domNode, 'display', 'block');
          domClass.remove(sidebarCollapseDiv, 'open');
          domClass.add(sidebarCollapseDiv, 'close');
        } else {
          //domStyle.set(sidebarCollapseDiv, 'display', 'none');
          domStyle.set(leftDiv.domNode, 'display', 'none');
          domClass.remove(sidebarCollapseDiv, 'close');
          domClass.add(sidebarCollapseDiv, 'open');
        }
        leftDiv.resize();
        //domStyle.set(dom.byId('mapDiv'), 'width', '100%');
        dijit.byId('mainDiv').resize();
        _map.resize(true);
        _map.reposition();
        //dijit.byId("mapDiv").refresh();
      });
      domStyle.set(sidebarCollapseDiv, 'display', 'block');
});


The function that is called to connect the onClick event should do something similar to the code below. Again the trick to figure out what lib to use to get access to the dom style and manage the display and add or remove the css class for "open" and "close"

domStyle.set(leftDiv.domNode, 'display', 'block');
domClass.remove(sidebarCollapseDiv, 'open');
domClass.add(sidebarCollapseDiv, 'close');



And would the other code you supplied (var titlePane = dijit.byId("paneEditor");...) go into that same function?


You only need this if you want to also automatically open a title pane by default such as the legend for example otherwise it will open the left panel but keep the state of the title panes as is.
0 Kudos
RyanWhiley
New Contributor
Hi Diana,

Thanks for your help, but I'm still quite confused.  So I would the code in my html like this
<div id="sidebarCollapseButton" class="sidebarCollapseButton.close" style="display:none;"></div>

<div id="leftDiv"  data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region:'left'" style="display:block;">   
  <div id="paneLayers"  class="titlePane" onClick="openSide();" data-dojo-type="dijit/TitlePane" data-dojo-props="title: 'Layers', open: true" >
    <div id="divLegendHolder"></div>
  </div>
  <div id="paneEditor"  class="titlePane" data-dojo-type="dijit/TitlePane" data-dojo-props="title: 'Editor Tools', open: false" >
    <div id="editToolbarPane" class = "toolbarContentPane_TitlePane" dojotype="dijit/layout/ContentPane" region="top">    
      <div id="editToolbar" data-dojo-type="dijit/Toolbar">
        <div id ="templateDiv"> </div>
  <div id ="editorDiv"> </div>
      </div>
    </div> 
    <div id="editTemplateHolder" ></div>
  </div>
</div>

With the same css you provided.  And this is placed in my leftPanel correct?  But then what code would I use to place the trigger button in the toolbar?

I then placed the js code you gave me (require(["dojo/dom",...) in my html file.  Or is that the js that needs to be in my init? And do I have to call that function from anywhere in my html?
Then the onClick event calls this function
function openSide () { 
  domStyle.set(leftDiv.domNode, 'display', 'block');
domClass.remove(sidebarCollapseDiv, 'open');
domClass.add(sidebarCollapseDiv, 'close');
}
which is in my main js file.  Is there more code that I have to add to that function to make it work properly?  It wasn't working how I anticipated so I want to see if I've placed the right code in the right places and altered to the code correctly.  It was kind of looking like this was going to turn into something similar to an accordion. And I was wondering if that was going to be the case or if I am able to alter it to make it more like Nliu's example. 

Thanks Diana!
Ryan
0 Kudos
DianaBenedict
Occasional Contributor III
Ryan

Try this code below. Open a new blank html page and cut and paste. It should mostly work other than the CSS and the images that are both referenced in the html and css must be hooked up else remove those parts if needed (ex) iconClass, etc.. Note: you will want to either remove references to css classes in my code or add the css in your file

[HTML]
<!--<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">-->
<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <meta http-equiv="X-UA-Compatible" content="IE=7,8,9,10" />
    <!--The viewport meta tag is used to improve the presentation and behavior of the samples on iOS devices-->
    <meta name="viewport" content="initial-scale=1, maximum-scale=1,user-scalable=no"/> 
    <title>Web Editing Tool</title>
    <link rel="stylesheet" href="http://serverapi.arcgisonline.com/jsapi/arcgis/3.5/js/dojo/dijit/themes/claro/claro.css"/>
    <link rel="stylesheet" href="http://serverapi.arcgisonline.com/jsapi/arcgis/3.5/js/esri/css/esri.css" />
    <!--reference your custom css file below .. mine is called defaultStyle.css and it is located in a sub folder called css -->
    <link rel="stylesheet" href="css/defaultStyle.css"/>

    <!--The dojoConfig references Nliu's lib that, b/c of security/firewall I had to download the files to my server -->
    <script type="text/javascript">
      var dojoConfig = {
        parseOnLoad: true,
        packages: [
          { "name": "agsjs",
            "location": location.pathname.replace(/\/[^/]+$/, "")+'/lib/agsjs/Build_2_04'
          }]
      };
    </script>
    <script type="text/javascript" src="http://serverapi.arcgisonline.com/jsapi/arcgis/3.5"></script>  
  <script type="text/javascript">
    require(["dojo/ready", "dojo/dom", "dojo/dom-construct", "dojo/dom-class", "dojo/dom-style", "dojo/on",
        "esri/map", "esri/geometry/Extent", "esri/layers/ArcGISDynamicMapServiceLayer", "agsjs/dijit/TOC",
        "dijit/layout/BorderContainer", "dijit/layout/ContentPane", "dijit/TitlePane", "dijit/layout/TabContainer",
        "dojo/fx", "dijit/form/DropDownButton", "dijit/form/Button", "dojo/domReady!"
        ],
      function (ready, dom, domConstruct, domClass, domStyle, dojoOn, esriMap, esriExtent, ArcGISDynamicMapServiceLayer, agsjsTOC) {
        ready(function () {
          //set map as a global for this example ... so the code below will work; note you will need to initialize the map at some point
          var map;

          //set up the sidebar collapse
          var sidebarCollapseDiv = dom.byId('sidebarCollapseButton');
          var leftDiv = dijit.byId("leftDiv");
          //connect the click event for the sidebarCollapseDiv
          dojoOn(sidebarCollapseDiv, 'click', function (evt) {
            if (leftDiv.domNode.style.display === "none") {
              domStyle.set(leftDiv.domNode, 'display', 'block');
              domClass.remove(sidebarCollapseDiv, 'open');
              domClass.add(sidebarCollapseDiv, 'close');
            } else {
              domStyle.set(leftDiv.domNode, 'display', 'none');
              domClass.remove(sidebarCollapseDiv, 'close');
              domClass.add(sidebarCollapseDiv, 'open');
            }
            leftDiv.resize();
            //resize the map div
            dijit.byId('mainDiv').resize();
            if (map) {
              map.resize(true);
              map.reposition();
            }
          });
          domStyle.set(sidebarCollapseDiv, 'display', 'block');

          var initialExtent = new esriExtent({
            "xmin": -9549126.170705408,
            "ymin": 4612089.574063576,
            "xmax": -9542304.165930964,
            "ymax": 4617851.015070564,
            "spatialReference": {
              "wkid": 102100
            }
          });
          map = new esriMap("mapDiv", {
            "extent": initialExtent
          });


          //Add the terrain service to the map. View the ArcGIS Online site for services http://arcgisonline/home/search.html?t=content&f=typekeywords:service   
          var basemap = new ArcGISDynamicMapServiceLayer("http://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer");
          map.addLayer(basemap);

          var census = new ArcGISDynamicMapServiceLayer("http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Demographics/ESRI_Census_USA/MapServer", {
            "id": "census",
            "opacity": 0.8
          });
          var safety = new ArcGISDynamicMapServiceLayer("http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/PublicSafety/PublicSafetyOperationalLayer...", {
            "id": "publicsafety",
            "opacity": 0.8
          });

          dojo.connect(map, 'onLayersAddResult', function (results) {
            var toc = new agsjs.dijit.TOC({
              "map": map,
              "layerInfos": [{
                "layer": safety,
                "title": "Safety Operations"
              }]
            }, 'divBaseLegendHolder');
            toc.startup();

            var toc2 = new agsjs.dijit.TOC({
              "map": map,
              "layerInfos": [{
                "layer": census,
                "title": "Census"
              }]
            }, 'divLegendHolder');
            toc2.startup();

            safety.setVisibleLayers([2, 5, 8, 11]);

          });
          map.addLayers([census, safety]);

        });
       
        //this is the function that you can use to show the legened using a button onClick event.
        // For the life of me I am unable to get this to connect. Play with this code and see what you can do.
        // maybe you will want to create a different script tag.  Usually I seperate all my code out but it is harder to show proof of concept that way
        function showLegend() {
          var sidebarCollapseDiv = dom.byId('sidebarCollapseButton');
          var leftDiv = dom.byId("leftDiv");
          if (leftDiv.domNode.style.display === "none") {
            domStyle.set(leftDiv.domNode, 'display', 'block');
            domClass.remove(sidebarCollapseDiv, 'open');
            domClass.add(sidebarCollapseDiv, 'close');
          }
          var titlePane = dijit.byId("paneEditor");
          if (!titlePane.open) { titlePane.toggle(); }
        }
      });
  </script>
</head> 
  <body class="claro">
    <div id="mainDiv" data-dojo-type="dijit/layout/BorderContainer" data-dojo-props="design:'headline', gutters:false">
    <!--Below is my header information, you will want to create your own header to satisfy your application -->   
      <div id="headerContainer" class="headerDivDefault" data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region:'top'" >
     <table cellpadding="0" cellspacing="0" style="width:100%;">
          <tr>
            <td  style="width:150px; ">
           <div id="leftlogo">
            <!-- <a href="http://www.usgs.gov/"><img class="usgsLogo"  src="images/USGSLogo_55px.jpg" alt="USGS - science for a changing world" title="U.S. Geological Survey Home Page"/></a> -->
           </div>
            </td> 
            <td style=" width:auto; vertical-align:middle;">
              <div id="theTitle"class="headerTitle">
             Web Tool
           </div>            
            </td>     
          </tr>
        </table>
    </div> 
      <div id="sidebarCollapseButton" class="sidebarCollapseButton close" style="display:none;"></div>
      <div id="mapDiv" data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region:'center'">
     <!--Note that I  used a DropDownButton control so that I could attach the legend to a dropdown tooltip dialog instead of adding it to the leftDiv ..
          You might want to use a dijit button instead if you are not interested in the dropdown effect and would rather add to the left panel
        <button data-dojo-type="dijit/form/Button" id="showLayersButton" type="button"  data-dojo-props="title:'Show Layers', iconClass:'baseMapIcon', showLabel: true, disabled: false, onClick:function(){showLegend();}">Show Layers</button>
        -->   
        <div id="baseLayersDropDown" class="baseLayersDiv" data-dojo-type="dijit/form/DropDownButton" data-dojo-props="iconClass:'baseMapIcon',label:'Layers', showLabel: true, disabled: false">
     <!--Below is my example of how I was able to attach the legend to a dropdown tooltip dialog .. delete this code and above if you want to attach the legend to the left panel titlePane -->   
      <div data-dojo-type="dijit/TooltipDialog" class="tooltipDialogLayers">
      <div id="divBaseLegendHolder" ></div>
     </div>
    </div>
      </div>
   <div id="leftDiv"  data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region:'left'" style="display:block;">  
     <!--I use a titlePane dijit instead of an accordion .. your call change as needed  or just stack all your divs on top w/o a pane seperating them within the ContentPane only -->   
    <div id="paneLayers"  class="titlePane" data-dojo-type="dijit/TitlePane" data-dojo-props="title: 'Layers', open: true" >
          <div id="divLegendHolder"></div>
    </div>
    <div id="paneEditor"  class="titlePane" data-dojo-type="dijit/TitlePane" data-dojo-props="title: 'Editor Tools', open: false" >
      <div id ="templateDiv"> </div>
          <div id ="editorDiv"> </div>
    </div>
      </div>
    </div>
  </body>
</html>   
[/HTML]
0 Kudos
RyanWhiley
New Contributor
Hi Diana,

I'm still running into some issues.  I used your code and altered it as much as I could to try and make it work, but nothing really shows up on the page.  I've made a fiddle showing the problem. [url=http://jsfiddle.net/K32Qu/]Fiddle[/url].  Also, when I looked through the console on Firebug an error was showing up that said "NS_ERROR_DOM_BAD_URI: Access to restricted URI denied".  I'm not exactly sure what that means.  But I placed console.logs throughout the code and noticed that the js stops just before or during the "require(["dojo/ready",..." syntax and so I'm thinking that the two are probably related.  I don't know if this is an error that has an easy fix, but I'm just not sure how I would go about resolving it myself.



Thanks for all your help so far.
Ryan
0 Kudos
DianaBenedict
Occasional Contributor III
Ryan

I have updated the fiddle that you sent. Essentially, the css and html needs to be set up correctly. You can not refernce classes that are not part of your css ... the page will not load correctly. Also, I noticed that you instantiated the map 2x. I made that fix as well since this is actually where the error that you were seeing was coming from.

you will still need to make sure that you are referencing the agsjs library correctly. I have updated the fiddle to redirect to the xdomain for the lib but that still doesn't appear to work correctly. You might need to get the libs locally or figure out how to reference correctly from their location on the server.

Currently the page loads and data loads but NOT the TOC due to the agsjs lib not being reference correctly.
0 Kudos