Hi all
My calcite-button component sometimes does not load (it's annoyingly temperamental). The error is receive is: "Failed to construct 'URL': Invalid base URL".
Any help in fixing this error would be appreciated!
I am using React (vite) and I want a button that opens a popover, which then has inputs to filter the feature layer.
Additionally, the commented out layer.Views.find() does not work and I instead have to hard code layerView.items[0] which isn't ideal.
Code sample is below:
Header.jsx which is called as a component in Main.jsx
import React, { useState, useEffect } from 'react';
import './styles/header.css';
import './styles/style.css';
import * as reactiveUtils from "@arcgis/core/core/reactiveUtils.js";
import LayerView from "@arcgis/core/views/layers/LayerView.js";
import "@esri/calcite-components/dist/calcite/calcite.css";
import "@esri/calcite-components/dist/components/calcite-button";
import "@esri/calcite-components/dist/components/calcite-popover";
import "@esri/calcite-components/dist/components/calcite-input";
import "@esri/calcite-components/dist/components/calcite-input-number";
import "@esri/calcite-components/dist/components/calcite-input-message";
import { CalciteButton, CalcitePopover, CalciteInputNumber, CalciteInputMessage } from "@esri/calcite-components-react";
async function filterConfidence(webmap, view, minVal, maxVal) {
const psLayer = webmap.layers.find((layer) => layer.title === "PS");
const psLayerView = view.layerViews.items[0];
//const psLayerView = view.layerViews.find((layer) => layer.title === "PS");
console.log(psLayerView)
await reactiveUtils.whenOnce(() => !LayerView.updating);
let expression = ''
console.log(minVal, maxVal);
if ((minVal !== null || minVal !== '') && (maxVal !== null || maxVal !== '')) {
expression = "coherence <= " + maxVal + " AND coherence >= " + minVal
console.log(1)
} else if ((minVal !== null || minVal !== '') && (maxVal === null || maxVal === '')) {
expression = "coherence >= " + minVal
console.log(2)
} else if ((maxVal !== null || maxVal !== '') && (minVal === null || minVal === '')) {
expression = "coherence <= " + maxVal
console.log(3)
}
console.log(expression)
try {
if (expression !== '') {
psLayerView.filter = {
where: expression
};
}
} catch(error) {
console.log("Filter failed: ", error);
}
}
function Header(props) {
const [minValue, setMinValue] = useState(null);
const [maxValue, setMaxValue] = useState(null);
function InputMessage({minValue, maxValue}) {
if (maxValue < minValue) {
return <CalciteInputMessage icon="exclamation-mark-triangle" >Should be greater or equal to {minValue} </CalciteInputMessage>
}
}
useEffect(() => {filterConfidence(props.webmap, props.view, minValue, maxValue)}, [minValue, maxValue])
return (
<div id="header-panel">
<div className="header-title">
ENVI Inform Ground Motion Monitoring Service
</div>
<div id='header-container'>
<CalcitePopover label="Set confidence filter" reference-element="popover-button" auto-close placement="bottom" overlay-positioning="fixed" offset-distance="0" offset-skidding="0" pointer-disabled calcite-hydrated>
<div className='popover-container'>
<div className='popover-selectors'>
<div className='flex items-baseline'>
<div className='flex-auto'>
<div className='flex'>
<CalciteInputNumber placeholder="Minimum Confidence" alignment='start' number-button-type='vertical' scale='m' status='idle' step={0.1} min={0} max={1}
value={minValue} onCalciteInputNumberChange={(e) => setMinValue(e.target.value)} />
<CalciteInputNumber placeholder="Maximum Confidence" alignment='start' number-button-type='vertical' scale='m' status='idle' step={0.1} min={0} max={1}
value={maxValue} onCalciteInputNumberChange={(e) => setMaxValue(e.target.value)} />
value range: {minValue} to {maxValue}
<InputMessage minValue={minValue} maxValue={maxValue}></InputMessage>
</div>
</div>
</div>
</div>
</div>
</CalcitePopover>
<calcite-button appearance="outline-fill" id="popover-button" calcite-hydrated>
Filter by Confidence
</calcite-button>
</div>
</div>
)
}
export default Header;
Solved! Go to Solution.
Are you setting the assetPath in the index.js or main.js or whatever your app loads?
I had a similar issue and when diving into the source code, I found out that this solved my issue:
import { setAssetPath } from '@esri/calcite-components/dist/components';
setAssetPath('https://js.arcgis.com/calcite-components/1.9.2/assets');
That method is used to manually set the base path where assets can be found. If the script is used as "module", it's recommended to use "import. meta. url", such as "setAssetPath(import. meta. url)". Other options include "setAssetPath(document. currentScript. src)", or using a bundler's replace plugin to dynamically set the path at build time, such as "setAssetPath(process. env. ASSET_PATH)". But do note that this configuration depends on how your script is bundled, or lack of bundling, and where your assets can be loaded from. Additionally custom bundling will have to ensure the static assets are copied to its build directory.
If you have your assets stored locally in the build of your application, you need to use the setAssetPath('your-custom-dir'). In my solution, I just targeted directly to the assets of calcite-components from js.arcgis.com
I am also getting the same error when importing Calcite Components and rendering them in React (Vite). Did you make any progress in fixing this issue?
Here's my code:
import React, { useRef, useEffect, useState } from "react";
import { createRoot } from "react-dom/client";
import MapView from "@arcgis/core/views/MapView";
import Map from "@arcgis/core/Map";
import FeatureLayer from "@arcgis/core/layers/FeatureLayer";
import Legend from "@arcgis/core/widgets/Legend";
import PopupTemplate from "@arcgis/core/PopupTemplate";
import "@esri/calcite-components/dist/components/calcite-shell.js";
import "@esri/calcite-components/dist/components/calcite-shell-panel.js";
import "@esri/calcite-components/dist/components/calcite-panel.js";
import "@esri/calcite-components/dist/components/calcite-button.js";
import {
CalciteShell,
CalciteShellPanel,
CalcitePanel,
CalciteButton,
} from '@esri/calcite-components-react';
import "@esri/calcite-components/dist/calcite/calcite.css";
import "./App.css";
import MapMenu from "./components/MapMenu";
const hubData = {
agriculture: {
maps: [
{
name: "Caba Partnerships",
layers: ["https://services3.arcgis.com/Bb8lfThdhugyc4G3/arcgis/rest/services/CaBA_Partnerships/FeatureServer"]
},
{
name: "Rivers",
layers: ["https://services3.arcgis.com/Bb8lfThdhugyc4G3/arcgis/rest/services/OSOpenRiversCaBA2017/FeatureServer"]
}
]
}
};
function getLocation(country) {
switch (country) {
case "england":
return [-1.464854, 52.561928]
case "scotland":
return [56.4907, -4.2026]
case "ireland":
return [-7.6921, 53.1424]
case "wales":
return [52.1307, -3.7837]
case "uk":
return [52.561928, -1.464854]
default:
return [52.561928, -1.464854]
}
}
// set pretend url params for testing
//window.location.search = "?hub=agriculture&geog=england";
function App() {
const mapDiv = useRef(null);
const menuDiv = useRef(document.createElement("div"));
const menuDivRoot = useRef(null); // Ref to store the root
const floatingMenuRef = useRef(null);
const [menuVisible, setMenuVisible] = useState(true);
const toggleMenu = () => {
setMenuVisible(!menuVisible);
};
// const urlParams = new URLSearchParams(window.location.search);
// const hub = urlParams.get("hub") || "agriculture";
// const geog = urlParams.get("geog") || "england";
const hub = "agriculture";
const geog = "england";
useEffect(() => {
if (mapDiv.current) {
/**
* Initialize application
*/
const map = new Map({
basemap: "gray-vector",
});
const view = new MapView({
container: mapDiv.current,
map: map,
zoom: 6,
center: getLocation(geog),
padding: { left: 49 }
});
// Add a legend to the map
const legend = new Legend({
view: view,
});
view.ui.add(legend, "bottom-right");
view.popup.defaultPopupTemplateEnabled = true;
if (!menuDivRoot.current) {
menuDivRoot.current = createRoot(menuDiv.current);
}
menuDivRoot.current.render(
<MapMenu
Maps={hubData[hub].maps}
onMapSelect={(layers) => {
console.log(`Layers selected: ${layers}`);
// Remove all feature layers from the map
const layersToRemove = [...map.layers];
layersToRemove.forEach((layer) => {
console.log("Removing layer");
console.log(layer);
map.remove(layer);
});
layers.forEach((url) => {
// Add the selected feature layer to the map
const featureLayer = new FeatureLayer({
url: url
});
map.add(featureLayer);
});
// // Zoom to the layer's extent
// featureLayer.when(() => {
// view.goTo(featureLayer.fullExtent);
// });
}}
/>
);
// Add the DOM element to the view's UI
// view.ui.add(dataViewDiv.current, "top-right");
}
}, [menuDiv, mapDiv]);
useEffect(() => {
if (floatingMenuRef.current) {
floatingMenuRef.current.style.display = menuVisible ? 'block' : 'none';
}
}, [menuVisible]);
return (
<CalciteShell contentBehind>
<CalciteShellPanel slot="panel-start" displayMode="float">
<CalciteButton onClick={toggleMenu}>{menuVisible? 'Hide Menu' : 'Show Menu'}</CalciteButton>
<CalcitePanel className="floatingMenu" ref={floatingMenuRef}>
<CalcitePanel className="infoDiv" heading="Information"></CalcitePanel>
<div className="menuDiv" ref={menuDiv}></div>
</CalcitePanel>
</CalciteShellPanel>
<CalcitePanel>
<div className="mapDiv" ref={mapDiv}></div>
</CalcitePanel>
</CalciteShell>
);
}
export default App;
Unfortunately not. I gave up and switched tracks to MUI components.
Thanks for the update, that will likely be the way I go too.
Are you setting the assetPath in the index.js or main.js or whatever your app loads?
I had a similar issue and when diving into the source code, I found out that this solved my issue:
import { setAssetPath } from '@esri/calcite-components/dist/components';
setAssetPath('https://js.arcgis.com/calcite-components/1.9.2/assets');
That method is used to manually set the base path where assets can be found. If the script is used as "module", it's recommended to use "import. meta. url", such as "setAssetPath(import. meta. url)". Other options include "setAssetPath(document. currentScript. src)", or using a bundler's replace plugin to dynamically set the path at build time, such as "setAssetPath(process. env. ASSET_PATH)". But do note that this configuration depends on how your script is bundled, or lack of bundling, and where your assets can be loaded from. Additionally custom bundling will have to ensure the static assets are copied to its build directory.
If you have your assets stored locally in the build of your application, you need to use the setAssetPath('your-custom-dir'). In my solution, I just targeted directly to the assets of calcite-components from js.arcgis.com
This worked for me to update to calcite-components 2.x.x from 1.x.x. Thanks!
I haven't tested it but from other people's replies this fixes it so have marked it as the solution. Thanks!
I added this to my index.tsx file:
import { defineCustomElements } from "@esri/calcite-components/dist/loader";
import '@esri/calcite-components/dist/calcite/calcite.css';
import { setAssetPath } from '@esri/calcite-components/dist/components';
setAssetPath('https://js.arcgis.com/calcite-components/1.9.2/assets');
defineCustomElements(window);
After that had no issues.