I am using ArcGIS Experience Builder.
I'm working on a React component that toggles visibility of map layers in an Esri map. My widget works perfectly when I hardcode the layers configuration, but fails to add layers when I load the configuration from a config.json file.
Working Code Example:
import { React, AllWidgetProps } from 'jimu-core';
import { JimuMapViewComponent, JimuMapView } from 'jimu-arcgis';
import FeatureLayer from 'esri/layers/FeatureLayer';
import { IMConfig } from '../config';
const { useState, useEffect } = React;
const layersConfig = [
{
name: "Layer 1",
url: "https://example.com/arcgis/rest/services/Layer1/MapServer/0",
},
{
name: "Layer 2",
url: "https://example.com/arcgis/rest/services/Layer2/MapServer/0",
}
];
const Widget = (props: AllWidgetProps<IMConfig>) => {
const [jimuMapView, setJimuMapView] = useState<JimuMapView>(null);
const [layers, setLayers] = useState([]);
useEffect(() => {
if (props.config && props.config.layers) { //not necessary in this case
const initialLayers = layersConfig.map(layerConfig => ({
...layerConfig,
layer: new FeatureLayer({
url: layerConfig.url,
title: layerConfig.name,
visible: false
})
}));
console.log('Initial layers:', initialLayers);
setLayers([...initialLayers]);
}
}, [config.props]); //also not necessary in the working sample
const activeViewChangeHandler = (jmv: JimuMapView) => {
if(jmv) {
setJimuMapView(jmv);
layers.forEach(({ layer }) => jmv.view.map.add(layer));
}
};
const toggleLayerVisibility = (index) => {
const newLayers = layers.map((layer, idx) => {
if (idx === index) {
layer.layer.visible = !layer.layer.visible;
}
return layer;
});
setLayers(newLayers);
};
return (
<div>
{props.useMapWidgetIds && props.useMapWidgetIds.length === 1 && (
<JimuMapViewComponent props.useMapWidgetId={props.useMapWidgetIds[0]} onActiveViewChange={activeViewChangeHandler} />
)}
<div>
{layers.map((layer, index) => (
<label key={index}>
<input
type="checkbox"
checked={layer.layer.visible}
onChange={() => toggleLayerVisibility(index)}
/>
{layer.name}
</label>
))}
</div>
</div>
);
};
export default Widget;
Non-Working Code (using config.json):
// Similar to the above, but `layersConfig` is replaced with `props.config.layers`
useEffect(() => {
if (props.config && props.config.layers) {
const initialLayers = props.config.layers.map(layerConfig => ({ //<--- This changed!
...layerConfig,
layer: new FeatureLayer({
url: layerConfig.url,
title: layerConfig.name,
visible: false
})
}));
console.log('Initial layers:', initialLayers);
setLayers([...initialLayers]);
}
}, [props.config]);
config.json:
{
"layers": [
{
"name": "Layer 1",
"url": "hidden",
},
{
"name": "Layer 2",
"url": "hidden"
}
]
}
config.ts:
import { ImmutableObject } from 'seamless-immutable';
export interface LayerConfig {
name: string;
url: string;
}
export interface Config {
layers: LayerConfig[];
}
export type IMConfig = ImmutableObject<Config>;
Error Message:
[esri.WebMap] #add() The item being added is not a Layer or a Promise that resolves to a Layer.
This error occurs at the line where I try to add layers to the map:
layers.forEach(({ layer }) => jmv.view.map.add(layer));
Extra:
Here is initial layers for the working sample:
initial layers for working sample
And for the non-working:
initial layers for non working sample
Please help I've been trying to figure it out by different means for the past 10 hours...
Edit:
I would greatly appreciate any kind of help or suggestion, I can furnish more information if necessary.
Since I typecasted it into ImmutableObject, it added a bunch of other properties that obscured it and I suppose it wasn't identified as a layer, so I parsed it again:
useEffect(() => {
let layersConfig = [];
if (props.config && props.config.layers) {
// Parse the JSON string to JavaScript object
layersConfig = JSON.parse(JSON.stringify(props.config.layers));
const initialLayers = layersConfig.map(layerConfig => {
return {
...layerConfig,
layer: new FeatureLayer({
url: layerConfig.url,
title: layerConfig.name,
visible: false
})
};
});
setLayers([...initialLayers]);
}
}, [props.config]);