Select to view content in your preferred language

How do I add an Action Section to a whole LayerList Widget?

2873
11
Jump to solution
08-11-2017 02:06 PM
GregSalvador
New Contributor III

Howdy all,

An easy question for the pros:

In the "LayerList widget with actions" sample code (https://developers.arcgis.com/javascript/latest/sample-code/widgets-layerlist-actions/index.html) the code gives some actions to a layer. I've modified that example to add a "save" button that writes a cookie with the currently visible layers, so I can reload the page and have my new default layers be visible. However, when I add more layers, the save button shows up on each layer and each sublayer. I was looking for a way to have the save button appear once, but I don't really want it as part of any specific layer. Is there a way to do this?  

This is what I have so far:

//LayerListWidget initialization

var layerList = new LayerList({

view: view,

listItemCreatedFunction: onLayerWidgetStartup

});

function onLayerWidgetStartup(event) {

var item = event.item;

  item.actionsSections = [[{

  title: "Save Layer visibility",

  className: "esri-icon-save"

  }]];

//rest of stuff to write cookies

Since I'm calling onLayerWidgetStartup for each listItem, I get a Save Layer button on each.

0 Kudos
1 Solution

Accepted Solutions
RobertScheitlin__GISP
MVP Emeritus

Greg,

   As you probably know there is no overall menu for the LayerList widget so you would have to manually create something and add it to the widgets domNode. So it would take something like this:

        var layerList = new LayerList({
          view: view,
          // executes for each ListItem in the LayerList
          listItemCreatedFunction: defineActions
        });
        watchUtils.whenDefinedOnce(layerList, "viewModel", function(evt){
          var mydom = domConstruct.toDom("<a class='saveLink' href='#'>Save</a>");
          setTimeout(function(){
            domConstruct.place(mydom, query('.esri-layer-list')[0], "first");
            on(query('.saveLink')[0],'click', function(){
              console.info("save button clicked");
            });
          }, 200);
        });

View solution in original post

11 Replies
RobertScheitlin__GISP
MVP Emeritus

Greg,

  Are you saying that you do not want the children of the layer to get the action? If so then just check if the item.children property. If it is a collection then you are dealing with a parent element if it does not then you should not add the action.

0 Kudos
GregSalvador
New Contributor III

I don't want each Parent layer (or their children) to get the action. Instead, I would like for the whole LayerlistWidget to get the action.

The "save" action loops through each layer (and sublayer) and checks if the property visible=true. When it finds one that is on, it saves that layer.title into an array. The cookie is generated from the array.

edit: I don't need each layer to have a button that loops through the whole array, only one button for the whole widget

0 Kudos
RobertScheitlin__GISP
MVP Emeritus

Greg,

   As you probably know there is no overall menu for the LayerList widget so you would have to manually create something and add it to the widgets domNode. So it would take something like this:

        var layerList = new LayerList({
          view: view,
          // executes for each ListItem in the LayerList
          listItemCreatedFunction: defineActions
        });
        watchUtils.whenDefinedOnce(layerList, "viewModel", function(evt){
          var mydom = domConstruct.toDom("<a class='saveLink' href='#'>Save</a>");
          setTimeout(function(){
            domConstruct.place(mydom, query('.esri-layer-list')[0], "first");
            on(query('.saveLink')[0],'click', function(){
              console.info("save button clicked");
            });
          }, 200);
        });
GregSalvador
New Contributor III

Thank you very much Robert, this is exactly the kind of thing I was looking for. I didn't realize LayerList didn't have an overall menu. However, I'm having trouble making the code work. By adding a bunch of console.log("") lines, I've figured out that the watchUtils.whenDefinedOnce() function is not firing. Where in my code should this snippit go? I've placed it after

view.then(function () {

var layerList = new LayerList({

view: view,

listItemCreatedFunction: onLayerWidgetStartup //this is my defineActions function

});

view.ui.add(layerList, "top-left");

//Your snippit

with view being a mapView object. Thanks in advance for taking a look at this

0 Kudos
RobertScheitlin__GISP
MVP Emeritus

Greg,

   You should notice in my snippet I am not using listItemCreatedFunction as there is no need since you are not wanting to add an action to a individual layer. Just do as I did in my snippet.

0 Kudos
GregSalvador
New Contributor III

I added your snippit inside of the view.then() function, not into the listItemCreatedFunction. The reason I did is because the ARCGIS example for the LayerList widget (https://developers.arcgis.com/javascript/latest/sample-code/widgets-layerlist/index.html) initializes the widget inside that function. I simply added your snippit below that. When I tried to take the layerList initialization and your snippit outside of view.then() function, I got an error saying "object does not support method whenDefinedOnce". Very strange, since I know .whenDefinedOnce() is is part of watchUtils.

Note: I also included "esri/core/watchUtils" in the require bracket

Again, thank you for taking a look at this.

0 Kudos
RobertScheitlin__GISP
MVP Emeritus

Greg,

   You must have something off in your code then. Can you post what you have for review?

0 Kudos
GregSalvador
New Contributor III

Sure, here it is. I don't know how to put it in the fancy box with numbered lines like your replies.

require([

"esri/Map",

"esri/views/MapView",

"esri/layers/FeatureLayer",

"esri/layers/TileLayer",

"esri/widgets/LayerList",

"esri/core/watchUtils",

"dojo/dom",

"dojo/on",

"dojo/domReady!"

], function (

Map, MapView, FeatureLayer, TileLayer, LayerList, dom, on, watchUtils

) {

var featureLayer = new FeatureLayer({

url: "https://services.arcgis.com/V6ZHFr6zdgNZuVG0/arcgis/rest/services/Landscape_Trees/FeatureServer/0"

});

var transportationLyr = new TileLayer({

url: "https://server.arcgisonline.com/ArcGIS/rest/services/Reference/World_Transportation/MapServer",

// This property can be used to uniquely identify the layer

id: "streets",

});

//initialize the map object

var map = new Map({

basemap: "hybrid"

});

map.add(featureLayer);

map.add(transportationLyr);

//set up the viewing object and inital viewport

var view = new MapView({

container: "viewDiv", // Reference to the DOM node that will contain the view

map: map, // References the map object

extent: { // autocasts as new Extent()

xmin: -9177811,

ymin: 4247000,

xmax: -9176791,

ymax: 4247784,

spatialReference: 102100

}

});

//LayerListWidget initialization

var layerList = new LayerList({

view: view,

listItemCreatedFunction: onLayerWidgetStartup

});

view.ui.add(layerList, "top-left");

watchUtils.whenDefinedOnce(layerList, "viewModel", function (evt) {

var mydom = domConstruct.toDom("<a class='saveLink' href='#'>Save</a>");

setTimeout(function () {

domConstruct.place(mydom, query('.esri-layer-list')[0], "first");

on(query('.saveLink')[0], 'click', function () {

console.info("save button clicked");

});

}, 200);

});

view.then(function () {

layerList.on("trigger-action", function (event) {

var id = event.action.title;

//this is what is called when the save button is hit.

//If I undersand your snippit, I would paste over the

//the console.info("save button clicked"); command.

if (id === "Save Layer visibility") {

var item = event.item;

var operationalItemCollection = layerList.operationalItems;

var visibleLayersArray = CheckVisibleProperty(operationalItemCollection);

document.cookie = setCookie("CustomDefaultLayers", visibleLayersArray.join("|"));

}

});

});

//helper function to create visibleLayersArray

function CheckVisibleProperty(passedCollection) {

var passedArray = passedCollection.toArray();

var resultArray = new Array();

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

if (passedArray.visible === true) {

if (passedArray.children.length != 0) {

var tempArray = CheckVisibleProperty(passedArray.children);

resultArray = resultArray.concat(tempArray);

}

resultArray.push(passedArray.title);

}

}

return resultArray;

}

//I belive the onLayerWidgetStartup() can be commented out since the

//function only lets me read the cookies made and I know

//this part works

//helper function to initialize the layerList widget

function onLayerWidgetStartup(event) {

var item = event.item;

//initializeing the visibility of the layers on startup

item.visible = false;

if (checkCookie()) //check cookie is defined in the cookieMonster

{

var resultString = getCookie("CustomDefaultLayers");

if (resultString.indexOf(item.title) != -1) {

item.visible = true;

}

//defaultVisibleLayers is defined as a JSON object in another .js file

} else {

if (defaultVisibleLayers.indexOf(item.title) != -1) { //string.indexOf() reference https://www.w3schools.com/jsref/jsref_indexof.asp

item.visible = true;

}

}

}

});

0 Kudos
RobertScheitlin__GISP
MVP Emeritus

Greg,

   So what you will have to learn about JS coding (AMD style) is that your require array list needs to align with your variable list.

require([
"esri/Map",
"esri/views/MapView",
"esri/layers/FeatureLayer",
"esri/layers/TileLayer",
"esri/widgets/LayerList",
"esri/core/watchUtils",
"dojo/dom",
"dojo/on",
"dojo/domReady!"
], function (
Map, MapView, FeatureLayer, TileLayer, LayerList, watchUtils, dom, on
) {

The abc’s of AMD | ArcGIS Blog