Select to view content in your preferred language

Map Components - Home - LayerList

127
6
Jump to solution
a week ago
ToddGurnee
Occasional Contributor

Two part question here. I was hoping to get some help expanding on the functionality of both the Home component and also the LayerList component.

#1 - How can you change a layer's visibility setting when the user clicks the Home button? (Example: the users has turned on all the layers and I would like the Home button to not only return to the default extent but also the default layers visible.)

#2 - I would like to add the ZoomTo button to the layers in the LayerList. I can add the symbol to the layer but can't get it to trigger.  

I have been able to do these using widgets but struggling converting to components.

For simplicity sake the code I provided is just an expanded version of the code from ESRI's LayerList example

I appreciate any help

<!doctype html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=5.0" />
    <title>Map components LayerList Example</title>
    <link rel="icon" href="data:;base64,=" />
    <style>
      html,
      body {
        margin: 0;
      }
     
      .tool-container {
      display: flex;
      gap: 0.5rem;
    }
     
      arcgis-map {
        display: block;
        height: 100vh;
      }
    </style>

    <!-- Load Calcite Components-->
    <script type="module" src="https://js.arcgis.com/calcite-components/3.2.1/calcite.esm.js"></script>

    <!-- Load the ArcGIS Maps SDK for JavaScript -->
    <link rel="stylesheet" href="https://js.arcgis.com/4.33/esri/themes/dark/main.css" />
    <script src="https://js.arcgis.com/4.33/"></script>

   <script type="module" src="https://js.arcgis.com/4.33/map-components/"></script>
  </head>

  <body>
<arcgis-map
    item-id="237b9584339446a0b56317b5962a4971">
 
    <arcgis-home>
    </arcgis-home>

    <arcgis-layer-list
        position="bottom-left">
    </arcgis-layer-list>

</arcgis-map>

<script>
    async function load() {
   
    const Layer = await $arcgis.import("esri/layers/Layer");

    // Reference the map component
    const mapElem = document.querySelector("arcgis-map");
   
    mapElem.addEventListener("arcgisViewReadyChange", () => {
    }, { once: true });
    };

load();

    document.querySelector("arcgis-layer-list").listItemCreatedFunction = (event) => {
        const { item } = event;

        // Exclude group layers, otherwise the legend will be displayed twice
        if (item.layer.type != "group") {
          item.panel = {
            content: "legend",
            open: false,
          };
        }
      }
    </script>

(https://developers.arcgis.com/javascript/latest/references/map-components/arcgis-layer-list/)

0 Kudos
1 Solution

Accepted Solutions
Sage_Wall
Esri Regular Contributor

Hi @ToddGurnee ,

I noticed a couple things leading to the behavior described above.

For the Home component not responding, in your latest app you were listening for the "go" event using the "on()" method but these are properties and methods on the legacy widget not the web component.  Either creating a new button or listening to the Home component's "arcgisGo" method should fix that issue.

In your arcgisViewReadyChange event listener you were trying to get the view from "event.detail" you need to get it from "event.target", event detail is null. You could also use the reference to the element "arcgisMap".

The last thing I saw was in your query results you need to check to see if the returned geometry is a point.  Point's don't have extents and this was breaking the script.

This codepen has the updates I made:

https://codepen.io/sagewall/pen/WbQRyRy?editors=1010

Hope this helps 🙂 

 

 

View solution in original post

6 Replies
VenkataKondepati
Emerging Contributor

Part 1: Reset Layer Visibility on Home Button Click
The <arcgis-home> component triggers a zoom to the default extent, but doesn't reset layers by default. To do this, manually listen for the click on the Home button and reset layer visibility like this:

const homeBtn = document.querySelector("arcgis-home");

homeBtn.addEventListener("click", () => {
const view = document.querySelector("arcgis-map").view;

// Reset visibility for each layer to default (example: make all layers off)
view.map.layers.forEach((layer) => {
layer.visible = layer.title === "Basemap" ? true : false; // customize per your need
});
});


Replace the logic based on your layer names or default visibility needs.

Part 2: Add ZoomTo Action in LayerList Items
To add a ZoomTo button and make it functional, customize the listItemCreatedFunction:

document.querySelector("arcgis-layer-list").listItemCreatedFunction = (event) => {
const { item } = event;

// Skip group layers
if (item.layer.type !== "group") {
item.panel = {
content: "legend",
open: false,
};

item.actionsOpen = true;
item.actionsSections = [
[
{
title: "Zoom to layer",
className: "esri-icon-zoom-in-magnifying-glass",
id: "zoom-to-layer"
}
]
];
}
};

// Handle the ZoomTo action
document.querySelector("arcgis-layer-list").addEventListener("trigger-action", (event) => {
const actionId = event.action.id;
const layer = event.item.layer;
const view = document.querySelector("arcgis-map").view;

if (actionId === "zoom-to-layer" && layer.fullExtent) {
view.goTo(layer.fullExtent);
}
});

Ensure layers have fullExtent available (some service layers may require loading).

You can extend this to add more actions (e.g., toggle visibility, transparency).

0 Kudos
ToddGurnee
Occasional Contributor

Thanks for the quick response. I have implemented both of your code snippets. Adjusting the Home code to turn off the Trailheads layer. The magnifying glass shows up in the LayerList. I receive no errors but the neither of the Visibility or ZoomTo functionality get triggered.

I've added console.logs at various points in the code to gain some insight but even these never fire. My attempts with AI also lead to this same result. The code below is how I have implemented your code. Is there something I have missed or perhaps another way of going about this? Thanks again.


<!doctype html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=5.0" />
    <title>Map components LayerList Example</title>
    <link rel="icon" href="data:;base64,=" />
    <style>
      html,
      body {
        margin: 0;
      }
     
      .tool-container {
      display: flex;
      gap: 0.5rem;
    }
     
      arcgis-map {
        display: block;
        height: 100vh;
      }
    </style>

    <!-- Load Calcite Components-->
    <script type="module" src="https://js.arcgis.com/calcite-components/3.2.1/calcite.esm.js"></script>

    <!-- Load the ArcGIS Maps SDK for JavaScript -->
    <link rel="stylesheet" href="https://js.arcgis.com/4.33/esri/themes/light/main.css" />
    <script src="https://js.arcgis.com/4.33/"></script>

   <script type="module" src="https://js.arcgis.com/4.33/map-components/"></script>
  </head>

  <body>
<arcgis-map
    item-id="237b9584339446a0b56317b5962a4971">
 
    <arcgis-home>
    </arcgis-home>

    <arcgis-layer-list
        position="bottom-left">
    </arcgis-layer-list>

</arcgis-map>

<script>

  const homeBtn = document.querySelector("arcgis-home");

  homeBtn.addEventListener("click", () => {
  const view = document.querySelector("arcgis-map").view;

  // Reset visibility for each layer to default (example: make all layers off)
  view.map.layers.forEach((layer) => {
  layer.visible = layer.title === "Trailheads" ? true : false; // customize per your need
  });
  });

  document.querySelector("arcgis-layer-list").listItemCreatedFunction = (event) => {
    const { item } = event;

    // Skip group layers
  if (item.layer.type !== "group") {
    item.panel = {
    content: "legend",
    open: false,
  };

  item.actionsOpen = true;
  item.actionsSections = [
    [
    {
    title: "Zoom to layer",
    className: "esri-icon-zoom-in-magnifying-glass",
    id: "zoom-to-layer"
    }
    ]
    ];
    }
    };

  // Handle the ZoomTo action
  document.querySelector("arcgis-layer-list").addEventListener("trigger-action", (event) => {
    const actionId = event.action.id;
    const layer = event.item.layer;
    const view = document.querySelector("arcgis-map").view;

    if (actionId === "zoom-to-layer" && layer.fullExtent) {
      view.goTo(layer.fullExtent);
    }
  });

</script>
0 Kudos
Sage_Wall
Esri Regular Contributor

Hi @ToddGurnee ,

Take a look at this sample it shows how to add custom actions to the layer list.  Your last code snippet is really close, it looks like you just need to rename the event your listening too.  There is a slight difference between the Core API event "trigger-action" and the component event's name "arcgisTriggerAction".  Try renaming the event listener to listen to "arcgisTriggerAction" as shown here: 

https://developers.arcgis.com/javascript/latest/sample-code/layer-list-actions/

0 Kudos
ToddGurnee
Occasional Contributor

Thank you Sage_Wall. You got me pointed in the right direction as far as the LayerList. I've gotten that code to work. It includes some code for another app of mine that has filtered jurisdictional boundaries in the webmap. So now it goes to the geometry and not full extent.

But I am unable to get anything to even trigger (arcgisTriggerAction or arcgisViewReadyChange or numerous other things) using with the Home component. If you have any additional thoughts on that I would greatly appreciate it.

Below is my updated code:

<!doctype html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=5.0" />
    <title>Map components LayerList Example</title>
    <link rel="icon" href="data:;base64,=" />
    <style>
      html,
      body {
        margin: 0;
      }
     
      .tool-container {
      display: flex;
      gap: 0.5rem;
    }
     
      arcgis-map {
        display: block;
        height: 100vh;
      }
    </style>

    <!-- Load Calcite Components-->
    <script type="module" src="https://js.arcgis.com/calcite-components/3.2.1/calcite.esm.js"></script>

    <!-- Load the ArcGIS Maps SDK for JavaScript -->
    <link rel="stylesheet" href="https://js.arcgis.com/4.33/esri/themes/light/main.css" />
    <script src="https://js.arcgis.com/4.33/"></script>

   <script type="module" src="https://js.arcgis.com/4.33/map-components/"></script>
  </head>

  <body>
<arcgis-map
    item-id="237b9584339446a0b56317b5962a4971">
 
    <arcgis-home>
    </arcgis-home>

    <arcgis-layer-list
        position="bottom-left">
    </arcgis-layer-list>

</arcgis-map>

<script type="module">
  const arcgisMap = document.querySelector("arcgis-map");

  arcgisMap.addEventListener("arcgisViewReadyChange", (event) => {
    const view = event.detail;

    if (!view) {
      console.warn("View not ready");
      return;
    }

    console.log("View is ready via arcgisViewReadyChange");

    const homeWidget = view.ui.find("home");

    if (homeWidget) {
      console.log("Home widget found");

      homeWidget.on("go", () => {
        console.log("Home button clicked");

        view.map.layers.forEach((layer) => {
          console.log("Layer:", layer.title);
          layer.visible = layer.title === "Trailheads" ? true :
                          layer.title === "Trails" ? false :
                          layer.visible;
        });
      });
    } else {
      console.warn("Home widget not found in view.ui");
    }
  });

  // Layer list logic
  const arcgisLayerList = document.querySelector("arcgis-layer-list");

  arcgisLayerList.listItemCreatedFunction = (event) => {
    const { item } = event;
    if (item.layer.type !== "group") {
      item.panel = { content: "legend", open: false };
      item.actionsOpen = true;
      item.actionsSections = [[
        {
          title: "Zoom to layer",
          className: "esri-icon-zoom-in-magnifying-glass",
          id: "zoom-to-layer"
        }
      ]];
    }
  };

  arcgisLayerList.addEventListener("arcgisTriggerAction", async (event) => {
  const { id } = event.detail.action;
  const clickedLayer = event.detail.item.layer;

  if (id !== "zoom-to-layer") return;

  const view = await document.querySelector("arcgis-map").view;

  try {
    const layerView = await view.whenLayerView(clickedLayer);
    await layerView.when(); // wait until it's fully loaded

    // Try getting filter from layerView
    let where = "1=1"; // default
    if (layerView.filter && layerView.filter.where) {
      where = layerView.filter.where;
    } else if (clickedLayer.definitionExpression) {
      where = clickedLayer.definitionExpression;
    }

    // Build the query using the determined filter
    const query = clickedLayer.createQuery();
    query.where = where;
    query.returnGeometry = true;
    query.outSpatialReference = view.spatialReference;

    const result = await clickedLayer.queryFeatures(query);

    if (result.features.length) {
      // Calculate the union extent of all features
      let extent = result.features[0].geometry.extent;

      for (let i = 1; i < result.features.length; i++) {
        extent = extent.union(result.features[i].geometry.extent);
      }

      if (extent) {
        await view.goTo(extent.expand(1.2));
      } else {
        console.warn("Features found but no extent available.");
      }
    } else {
      console.warn("No features found with the applied filter.");
    }
  } catch (error) {
    if (error.name !== "AbortError" && error.name !== "view:goto-interrupted") {
      console.error("Zoom error:", error);
    } else {
      console.info("Zoom was interrupted:", error.message);
    }
  }
});



</script>

 

 

0 Kudos
Sage_Wall
Esri Regular Contributor

Hi @ToddGurnee ,

I noticed a couple things leading to the behavior described above.

For the Home component not responding, in your latest app you were listening for the "go" event using the "on()" method but these are properties and methods on the legacy widget not the web component.  Either creating a new button or listening to the Home component's "arcgisGo" method should fix that issue.

In your arcgisViewReadyChange event listener you were trying to get the view from "event.detail" you need to get it from "event.target", event detail is null. You could also use the reference to the element "arcgisMap".

The last thing I saw was in your query results you need to check to see if the returned geometry is a point.  Point's don't have extents and this was breaking the script.

This codepen has the updates I made:

https://codepen.io/sagewall/pen/WbQRyRy?editors=1010

Hope this helps 🙂 

 

 

ToddGurnee
Occasional Contributor

Thank you so much for your help! 

0 Kudos