What can I use instead of dojo/aspect and dojo/query - ArcGIS 4.19 API

1289
5
Jump to solution
05-11-2021 03:01 PM
WilsonIonara
New Contributor III

I need to have access to the dom of the legend after it is rendered every time a user makes a query, and I am using ESM modules with the new, 4.19 API. I am trying to stay away from dojo. So how can I replace dojo/aspect and dojo/query with something that works with the new API? My project is a node project with webpack, with no specific framework. Here it is what worked before using the 4.17 API and AMD modules Thank you for any help!

 

 

 

watchUtils.when(legend, "container", function () {
    aspect.after(legend, "scheduleRender", function (response: any) {
      if (query('.esri-legend__layer-cell--info')[0]) {
        var legendinfotitle2 = document.querySelectorAll("h3.esri-widget__heading.esri-legend__service-label");
        var legendinfo = document.querySelectorAll(".esri-legend__layer-cell--info")[0];
        if (legendinfo) {
          legendinfotitle2[0].innerHTML = " ";
        }
        setTimeout(function () {
          // changeUrlLabel()
          if (view.popup.selectedFeature != undefined) {
            var selectedProgramTitle = view.popup.selectedFeature.attributes.ProgramName;
          }
          if (legendinfo) {
            if (legend.activeLayerInfos) {
              if (selectedProgramTitle != "Northeast Texas Habitat Incentive Program" && selectedProgramTitle != "Texas Longleaf Conservation Assistance Program"
                && selectedProgramTitle != "Longleaf Pine Initiative") {
                legendinfotitle2[0].innerHTML = " ";
              }
              else {
                legendinfotitle2[0].innerHTML = selectedProgramTitle;
              }
            }
          }
          document.getElementById("legendDiv").style.display = "block";
        }, 400);
      }
    });
  });

 

 

 

 

Tags (1)
0 Kudos
1 Solution

Accepted Solutions
ReneRubalcava
Frequent Contributor

As much as I'm a fan of AOP, you don't really need it in this case. It looks like you are updating the titles in the legend. You can wait until the query is done, watch the layer or watch for changes in the activeLayerInfos collection, if that will be updated. Change the title of the layer and it will be reflected in the legend.

You should avoid modifying the DOM of widgets as there's no guarantee the changes will stick. That's why you're waiting for the scheduleRender method to fire, but I think you can change your workflow to avoid that use case.

View solution in original post

0 Kudos
5 Replies
ReneRubalcava
Frequent Contributor

As much as I'm a fan of AOP, you don't really need it in this case. It looks like you are updating the titles in the legend. You can wait until the query is done, watch the layer or watch for changes in the activeLayerInfos collection, if that will be updated. Change the title of the layer and it will be reflected in the legend.

You should avoid modifying the DOM of widgets as there's no guarantee the changes will stick. That's why you're waiting for the scheduleRender method to fire, but I think you can change your workflow to avoid that use case.

0 Kudos
WilsonIonara
New Contributor III

Yes, you are right Rene. I tried a solution that works much better. I just waited for the legend elements to be updated. But I am still curious on what to use if we ever need something like aspect? let's say we cannot use watchUtils on a widget, what could we use?  Thank you! Here is my updated code:

 

 

watchUtils.when(legend, "activeLayerInfos.length", getElementsAfterUpdate);
function getElementsAfterUpdate() {
 if (legend.activeLayerInfos.length > 0) {
      legend.activeLayerInfos.forEach(layerInfo => {
          watchUtils.when(layerInfo, "legendElements", function () {
              updateLegend(layerInfo)
          });
      });
  }
}
function updateLegend(layer:any)  {
  if (legend.activeLayerInfos) {
  legend.activeLayerInfos.forEach(e => {
    if (e.title != "Northeast Texas Habitat Incentive Program" && e.title != "Texas Longleaf Conservation Assistance Program"
    && e.title != "Longleaf Pine Initiative") {
   e.title = " ";
    }
    });
 }
}

 

 

0 Kudos
ReneRubalcava
Frequent Contributor

I don't think there's many JS AOP libs out there, other than AspectJS, but I don't know if it's still active and maintained. You could probably use JavaScript Proxy for similar AOP type work to intercept methods.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/Proxy/apply

I haven't tried this though, so can't say for sure. However, at the moment our base Accessor used for almost all the JSAPI does not work well with Proxy, but we are looking to update the in the future.

0 Kudos
WilsonIonara
New Contributor III

I probably won't need to use JavaScript Proxy but it is good to know it exists and that you will update it in the future. Thank you!

0 Kudos
by Anonymous User
Not applicable

so, I have a similar issue:

I need to remove individual features from the service list based on what is actually displayed on the map. I had this working with:

aspect.after(legend, 'render', function (target) {
			// Handle Show/Hide for specific items after render
			target.children.forEach(function (serviceNode) {
				if (serviceNode.properties.class === 'esri-legend__service' && (serviceNode.children[0].text === 'Forest Types')) {
					serviceNode.children.forEach(function (layerNode) {
						if (layerNode.properties.class === 'esri-legend__layer') {
							layerNode.children.forEach(function (layerTableNode) {
								var layerTableBodyNode = layerTableNode.children[1];
								layerTableBodyNode.children.forEach(function (row) {
									if (row.properties.class.includes('esri-legend__layer-row')) {
										switch (serviceNode.children[0].text) {									case 'Forest Types':
												row.properties.classes = { hidden: !overlayFeatures.forestTypes.includes(row.children[1].text) };
												break;
										}
									}
								});
							});
						}
					});
				}
			});
			// Return the 'virtual' DOM to be rendered to the screen
			return target;
		});

 

I have this mostly working with code similar to the answer provided by @WilsonIonara, but if the user pans/zooms the map, the legend resets and all items are displayed again, and it doesn't appear that the JavaScript Proxy option will work (I'm not calling legend.render() at any point, so I can't proxy the request). Is there any event in the Legend widget (or Accessor), that I can hook into with an on() call or something similar that lets me know when the legend is updated? I've searched through the API docs, and attempted to look through the minified source code that is available for download, but no luck as of yet.

 

Thanks and sorry for thread-jacking!

0 Kudos