I am running into trouble with trying to take the example of opening and closing a side widget programmatically.
I get an error of Minified React error #321; I tried removing multple instance of React and same problem.
Thanks if anybody can help.
This is my code.
/** @jsx jsx */
// import { React, AllWidgetProps, jsx } from 'jimu-core';
import { JimuMapViewComponent, JimuMapView } from 'jimu-arcgis';
import { TextArea } from 'jimu-ui';
import { Button, Icon } from 'jimu-ui'; // import components
import { Table } from 'jimu-ui';
import { Spinner } from 'reactstrap';
import { StarFilled } from 'jimu-icons/filled/application/star';
import Graphic from 'esri/Graphic';
import Point from 'esri/geometry/Point';
import PopupTemplate from 'esri/PopupTemplate';
import AttachmentQuery from 'esri/rest/support/AttachmentQuery';
import RelationshipQuery from 'esri/rest/support/RelationshipQuery';
import FeatureLayer from 'esri/layers/FeatureLayer';
import { Dropdown, DropdownItem, DropdownMenu, DropdownButton, Checkbox } from 'jimu-ui';
import { TextInput } from 'jimu-ui';
import { TextArea } from 'jimu-ui';
import { useState } from "react";
import { React, AllWidgetProps, getAppStore, appActions, ReactRedux, WidgetProps, WidgetManager, IMState,jsx } from 'jimu-core';
// import { Button, Label, Row, Col, Select, Option } from 'jimu-ui';
import './styles.css';
const iconNode = <StarFilled />;
const { useState, useEffect, } = React;
const { useSelector } = ReactRedux;
export default class Widget extends React.PureComponent<AllWidgetProps<any>, any> {
state = {
jimuMapView: null,
latitude: '',
longitude: '',
feature: { attributes:
{ GlobalID: null,
ID:'',
SignalNumber:'',
SignalCategory:'',
Hamlet: '' ,
Intersection1:'',
Intersection2:'',
SignalType:'',
Jurisdiction:'',
YearBuilt:'',
LatestBuild:'',
AssetCost:"",
LEDManufacturerdel:"",
NumberPhase:'',
CycleLength:'',
PoleType:'',
DetType:'',
SupportType:'',
F8LED:'',
F8Incan:'',
F12LED:'',
F12Incan:'',
Contractor:'',
LipaAccount:'',
email:'',
SignalNote:'',
}, layer: null },
workOrderFeatureLayer: null,
};
handleChange = (e) => {
const [file, setFile] = useState();
console.log(e.target.files);
setFile(URL.createObjectURL(e.target.files[0]));
};
sidebarWidget = () => {
// Establish state properties, initial values and their corresponding set state actions
const { useState, useEffect, } = React;
const { useSelector } = ReactRedux;
const [sidebarWidgetId, setSidebarWidgetId] = useState(null as string);
const [openCloseWidgetId, setOpenCloseWidgetId] = useState(null as string);
const [sidebarVisible] = useState(true as boolean);
const [openness, setOpenness] = useState(false as boolean);
const [appWidgets, setAppWidgets] = useState({} as Object);
const [widgetsArray, setWidgetsArray] = useState([] as Array<any>);
const [sidebarWidgetsArray, setSidebarWidgetsArray] = useState([] as Array<any>);
// Get the widget state - because the sidebar state may change in the runtime, via Redux's useSelector hook
const widgetState = useSelector((state: IMState) => {
const widgetState = state.widgetsState[sidebarWidgetId];
return widgetState;
});
// Update the appWidgets property once, on page load
useEffect(() => {
const widgets = getAppStore().getState().appConfig.widgets;
setAppWidgets(widgets);
}, []);
// Update the widgetsArray and sidebarWidgetsArray properties every time appWidgets changes
useEffect(() => {
if (appWidgets) {
const widgetsArray = Object.values(appWidgets);
setWidgetsArray(widgetsArray);
setSidebarWidgetsArray(widgetsArray.filter(w => w.uri === 'widgets/layout/sidebar/'));
}
}, [appWidgets]);
// Toggle the sidebar widget
function handleToggleSidebar () {
// If widget state's collapse property is true, collapse
if (widgetState && widgetState.collapse === true) {
getAppStore().dispatch(
appActions.widgetStatePropChange(
sidebarWidgetId,
"collapse",
!sidebarVisible
)
);
}
// If widget state's collapse property is false, expand
else if (widgetState && widgetState.collapse === false) {
getAppStore().dispatch(
appActions.widgetStatePropChange(
sidebarWidgetId,
"collapse",
sidebarVisible
)
);
} else {
console.log("Hey it's your console log");
}
};
// handleToggleSidebar()
// Load the widget class prior to executing the open/close actions
const loadWidgetClass = (widgetId: string😞 Promise<React.ComponentType<WidgetProps>> => {
if (!widgetId) return;
const isClassLoaded = getAppStore().getState().widgetsRuntimeInfo?.[widgetId]?.isClassLoaded;
if (!isClassLoaded) {
return WidgetManager.getInstance().loadWidgetClass(widgetId);
} else {
return Promise.resolve(WidgetManager.getInstance().getWidgetClass(widgetId));
}
};
// Open widget method
const handleOpenWidget = (): void => {
// Construct the open action, then run the loadWidgetClass method, dipatch the open action
// and, finally, set the openness to true
const openAction = appActions.openWidget(openCloseWidgetId);
loadWidgetClass(openCloseWidgetId).then(() => {
getAppStore().dispatch(openAction);
}).then(() => { setOpenness(true) });
};
const handleCloseWidget = (): void => {
// Construct the close action, then run the loadWidgetClass function, dipatch the close action
// and, finally, set the openness to false
const closeAction = appActions.closeWidget(openCloseWidgetId);
loadWidgetClass(openCloseWidgetId).then(() => {
getAppStore().dispatch(closeAction);
}).then(() => { setOpenness(false) });
};
// Handler for the openness toggle button
// const handleToggleOpennessButton = (): void => {
// // Check the openness property value and run the appropriate function
// if (openness === false) { handleOpenWidget(); }
// else if (openness === true) { handleCloseWidget(); }
// else { console.error(defaultMessages.opennessError) }
// };
// Handler for the sidebar selection
const handleSidebarSelect = evt => {
setSidebarWidgetId(evt.currentTarget.value);
};
// Handler for the open/close selection
const handleOpenCloseSelect = evt => {
setOpenCloseWidgetId(evt.currentTarget.value);
};
}
// ATTACHMENT FUNCTIONS
createAttachment = () => {
const self = this;
console.log(this);
// this function must be called from a user
// activation event (ie an onclick event)
// Create an input element
var inputElement = document.createElement('input');
// Set its type to file
inputElement.type = 'file';
// Set accept to the file types you want the user to select.
// Include both the file extension and the mime type
inputElement.accept = 'png';
// set onchange event to call callback when user has selected file
inputElement.addEventListener('change', (file) => {
const feature = this.state.feature;
const layer = feature.layer;
const form = new FormData();
form.set('attachment', inputElement.files[0]);
form.append('f', 'json');
layer
.addAttachment(feature, form)
.then(function (result) {
console.log('attachment added: ', result);
self.readAttachment(feature);
})
.catch(function (err) {
console.log('attachment adding failed: ', err);
});
});
// dispatch a click event to open the file dialog
inputElement.dispatchEvent(new MouseEvent('click'));
};
readAttachment = (feature) => {
const self = this;
const featureAttachments = document.getElementById('featureAttachments');
const fragment = document.createDocumentFragment();
const objectID = feature.attributes.ObjectID;
const query = new AttachmentQuery({
objectIds: [objectID],
});
featureAttachments.innerHTML = '';
feature.layer.queryAttachments(query).then((attachments) => {
if (attachments && attachments[objectID]) {
attachments[objectID].forEach((attachment) => {
const row = document.createElement('tr');
const attachmentID = document.createElement('td');
const name = document.createElement('td');
const contentType = document.createElement('td');
const size = document.createElement('td');
const keywords = document.createElement('td');
const removeFeature = document.createElement('td');
attachmentID.innerHTML = attachment.id;
name.innerHTML = attachment.name;
contentType.innerHTML = attachment.contentType;
size.innerHTML = attachment.size;
keywords.innerHTML = attachment.keywords;
removeFeature.innerHTML = 'Delete?';
removeFeature.style.cursor = 'pointer';
removeFeature.addEventListener('click', () => {
self.deleteAttachment(feature.layer, attachment.globalId);
});
row.append(attachmentID, name, contentType, size, keywords, removeFeature);
fragment.append(row);
});
featureAttachments.append(fragment);
}
});
};
updateSignal = () => {
const hamletField = document.getElementById('hamlet-field');
const feature = this.state.feature;
const featureLayer = this.state.feature.layer;
const edits = {
updateFeatures: [feature],
};
feature.attributes.Hamlet = hamletField.value;
featureLayer
.applyEdits(edits)
.then((editsResult) => {
console.log(editsResult);
})
.catch((error) => {
console.log('error = ', error);
});
};
deleteAttachment = (layer, attachmentID) => {
const self = this;
const edits = {
deleteAttachments: [attachmentID],
};
layer
.applyEdits(edits, { globalIdUsed: true, rollbackOnFailureEnabled: true })
.then((editsResult) => {
console.log(editsResult);
self.readAttachment(self.state.feature);
})
.catch((error) => {
console.log('error = ', error);
});
};
// WORK ORDER FUNCTIONS
createWorkOrder = () => {
const self = this;
const feature = this.state.feature;
let featureLayerURL = this.state.feature.layer.url;
const workOrderLayerNumber = feature.layer.relationships[0].relatedTableId;
const workOrderDescription = document.getElementById('work-order-description');
const workOrderGraphic = new Graphic({
attributes: {
ParentGlobalID: feature.attributes.GlobalID,
TextField1: workOrderDescription.innerHTML || 'things',
IntegerField: 12345678,
},
});
const edits = {
addFeatures: [workOrderGraphic],
};
if (this.state.workOrderFeatureLayer === null) {
this.state.workOrderFeatureLayer = new FeatureLayer({
url: `${featureLayerURL}/${workOrderLayerNumber}`,
});
}
this.state.workOrderFeatureLayer
.applyEdits(edits)
.then((editsResult) => {
self.readWorkOrder(feature);
})
.catch((error) => {
console.log('error = ', error);
});
};
readWorkOrder = (feature) => {
const editFormButton = document.getElementsByClassName('esri-icon-edit')
const self = this;
const featureWorkOrders = document.getElementById('workOrderEntries');
const fragment = document.createDocumentFragment();
const objectID = feature.attributes.ObjectID;
const query = new RelationshipQuery({
objectIds: [objectID],
outFields: ['*'],
});
featureWorkOrders.innerHTML = '';
feature.layer.queryRelatedFeatures(query).then((workOrders) => {
if (workOrders && workOrders[objectID]) {
workOrders[objectID].features.forEach((workOrder) => {
const row = document.createElement('tr');
const date = document.createElement('td');
const double = document.createElement('td');
const integer = document.createElement('td');
const text = document.createElement('td');
const removeFeature = document.createElement('td');
date.innerHTML = workOrder.attributes.DateField;
double.innerHTML = workOrder.attributes.DoubleField;
integer.innerHTML = workOrder.attributes.IntegerField;
text.innerHTML = workOrder.attributes.TextField1;
removeFeature.innerHTML = 'Delete?';
removeFeature.style.cursor = 'pointer';
removeFeature.addEventListener('click', () => {
self.deleteWorkOrder(feature, workOrder);
});
row.append(date, text, double, integer, removeFeature);
fragment.append(row);
});
featureWorkOrders.append(fragment);
}
});
};
updateWorkOrder = (feature) => {};
deleteWorkOrder = (feature, workOrder) => {
const self = this;
const featureLayerURL = feature.layer.url;
const workOrderLayerNumber = feature.layer.relationships[0].relatedTableId;
const edits = {
deleteFeatures: [workOrder],
};
if (this.state.workOrderFeatureLayer === null) {
this.state.workOrderFeatureLayer = new FeatureLayer({
url: `${featureLayerURL}/${workOrderLayerNumber}`,
});
}
this.state.workOrderFeatureLayer
.applyEdits(edits)
.then((editsResult) => {
self.readWorkOrder(feature);
})
.catch((error) => {
console.log('error = ', error);
});
};
// WIDGET CONTROLS
openSignalsForm = (feature) => {
const editSignalButton = document.querySelectorAll('.esri-icon-edit')
editSignalButton[0].addEventListener('click', () => {
alert("hello world")
});
this.setState({
feature: feature,
});
this.readAttachment(feature);
this.readWorkOrder(feature);
this.sidebarWidget();
//open sidemenu here
};
activeViewChangeHandler = (jmv: JimuMapView) => {
let self = this;
if (jmv) {
this.setState({
jimuMapView: jmv,
});
jmv.view.map.layers.forEach((layer) => {
if (layer.editingEnabled && layer.fields) {
let popupTemplate = new PopupTemplate({
// autocasts as new PopupTemplate()
title: '{Intersection1} @ {Intersection2}',
outFields: ['*'],
content: [
{
// add all the fields in the layer to the popup
// things could be filtered out here
// An alias could also be added to the popup
type: 'fields',
fieldInfos: layer.fields.map((field) => {
return { fieldName: field.name, label: field.alias };
}),
},
],
});
let openSignalsFormAction = {
title: `Edit ${layer.title}`,
id: 'open-signals-form',
className: 'esri-icon-edit',
};
popupTemplate.actions = [openSignalsFormAction];
layer.popupTemplate = popupTemplate;
} else {
}
});
// The function to execute when the zoom-out action is clicked
// This event fires for each click on any action
jmv.view.popup.on('trigger-action', function (event) {
// If the zoom-out action is clicked, fire the zoomOut() function
if (event.action.id === 'open-signals-form') {
let feature = jmv.view.popup.viewModel.selectedFeature;
self.openSignalsForm(feature);
}
});
jmv.view.on('pointer-move', (evt) => {
const point: Point = this.state.jimuMapView.view.toMap({
x: evt.x,
y: evt.y,
});
this.setState({
latitude: point.latitude.toFixed(3),
longitude: point.longitude.toFixed(3),
});
});
}
};
onViewsCreateHandler = () => {
console.log(this.state);
};
render() {
return (
<div className="widget-starter jimu-widget p-3 overflow-auto">
{this.props.hasOwnProperty('useMapWidgetIds') &&
this.props.useMapWidgetIds &&
this.props.useMapWidgetIds[0] && (
<JimuMapViewComponent
useMapWidgetId={this.props.useMapWidgetIds?.[0]}
onActiveViewChange={this.activeViewChangeHandler}
onViewsCreate={this.onViewsCreateHandler}
/>
)}
<div className="content-widget-box py-2 m-0 row">
<div className=" col-6 col-sm-6 col-md-4 col-lg-4 divider">
<div className="icj-box pt-2">
<h1>Location Information</h1>
<div className="row">
<div className="col-md-6">
<div className="w-100">
<div className="">
<span className="pr-2">ID</span>
<TextInput value={this.state.feature.attributes.ID} size="sm" type="text" />
</div>
</div>
</div>
<div className="col-md-6">
<div className="w-100">
<div className="">
<span className="pr-2">Cat.</span>
<TextInput
value={this.state.feature.attributes.SignalCategory}
size="sm"
type="text"
/>
</div>
</div>
</div>
<div className="col-md-12 pb-1">
<div>
<span className="pr-2">Jurisdiction</span>
<TextInput
value={this.state.feature.attributes.Jurisdiction}
size="sm"
type="text"
/>
</div>
</div>
<div className="col-md-12">
<div>
<span className="pr-2">Hamlet</span>
<TextInput
id="work-order-description"
value={this.state.feature.attributes.Hamlet}
size="sm"
/>
</div>
</div>
<div className="col-md-12">
<div>
<span className="pr-2">Intersection</span>
<TextInput
id="work-order-description"
value={
this.state.feature.attributes.Intersection1 +
' ' +
this.state.feature.attributes.Intersection2
}
size="sm"
className="mb-3"
/>
</div>
</div>
<div className="col-md-6">
<div className="">
<span className="pr-2">Signal Number</span>
<TextInput value={this.state.feature.attributes.SignalNumber} type="text" />
</div>
</div>
<div className="col-md-6">
<div className=" pb-1">
<span className="pr-2">Type</span>
<TextInput value={this.state.feature.attributes.SignalType} type="text" />
</div>
</div>
</div>
</div>
<div className="spd-box pt-2">
<div>
<div className="col-md-12 no-pad">
<h1>Signal Information</h1>
<div className=" pb-1">
<div className="">
<span className="pr-2">Support Type</span>
</div>
<div className="w-100">
<TextInput
value={this.state.feature.attributes.SupportType}
size="sm"
type="text"
/>
</div>
</div>
<div className=" pb-1">
<div className="">
<span className="pr-2">Pole Type</span>
</div>
<div className="w-100">
<TextInput
value={this.state.feature.attributes.PoleType}
size="sm"
type="text"
/>
</div>
</div>
<div className="pb-1">
<div className="">
<span className="pr-2">Det Type</span>
</div>
<div className="w-100">
<TextInput
value={this.state.feature.attributes.DetType}
size="sm"
type="text"
/>
</div>
</div>
{/* <div className="row checkBox-row">
<div className="col-md-6">
<span>8" LED </span>
<TextInput
value={this.state.feature.attributes.F8LED}
size="sm"
type="text"
/>
</div>
<div className="col-md-6">
<span>8" Incandescent </span>
<TextInput
value={this.state.feature.attributes.F8Incan}
size="sm"
type="text"
/>
</div>
<div className="col-md-6">
<span>12" LED </span>
<TextInput
value={this.state.feature.attributes.F12LED}
size="sm"
type="text"
/>
</div>
<div className="col-md-6">
<span>12" Incandescent </span>
<TextInput
value={this.state.feature.attributes.F12Incan}
size="sm"
type="text"
/>
</div>
</div> */}
</div>
</div>
</div>
<div className="acm-box">
<div>
<div>
<span>Asset Cost</span>
<TextInput value={this.state.feature.attributes.AssetCost} size="sm" type="text" />
</div>
<div>
<span>Maintenance</span>
<TextInput
value={this.state.feature.attributes.Maintenance}
size="sm"
type="text"
/>
</div>
</div>
</div>
<div className="clp-box">
<div>
<div>
<span>Contractor</span>
<TextInput value={this.state.feature.attributes.Contractor} size="sm" type="text" />
</div>
<div>
<span>Latest Build</span>
<TextInput
value={this.state.feature.attributes.LatestBuild}
size="sm"
type="text"
/>
</div>
<div>
<span>LIPA Account</span>
<TextInput
value={this.state.feature.attributes.LipaAccount}
size="sm"
type="text"
/>
</div>
</div>
</div>
</div>
<div className="col-6 col-sm-6 col-md-8 col-lg-8">
<div className="attachment-box">
<div className="attachment-controls">
<h1 className="mt-3">Attachments</h1>
<Button
type="primary"
className="mr-3"
onClick={this.createAttachment}
onChange={this.handleChange}
>
Add an attachment!
</Button>
</div>
<div className="img-box">
<img
className="img-attatchment"
></img>
<span className="pt-2"> 06/02/2022</span>
</div>
</div>
<Table className="my-3" size="sm" hover striped>
<thead>
<tr>
<th scope="col">ID</th>
<th scope="col">Name</th>
<th scope="col">Content Type</th>
<th scope="col">Size</th>
{/* <th scope="col">Keywords</th> */}
<th scope="col"></th>
</tr>
</thead>
<tbody id="featureAttachments"></tbody>
</Table>
<div className="row work-order">
<h1>Work Orders</h1>
<Button type="primary" onClick={this.createWorkOrder}>
Add Work Order
</Button>
</div>
<Table className="my-3" size="sm" hover striped>
<thead>
<tr>
<th scope="col">Date</th>
<th scope="col">Description</th>
<th scope="col">Quantity</th>
<th scope="col">Amount</th>
<th scope="col"></th>
</tr>
</thead>
<tbody id="workOrderEntries"></tbody>
</Table>
<div className="widget-demo noteSections px-3">
<div className="email-notes">
<div className="row flex-cr">
<div className="">
<label className="w-100">
<span>General Notes:</span>
<TextArea
className="mb-3 general-notes"
value={this.state.feature.attributes.SignalNote}
/>
</label>
</div>
<div className="">
<label className="w-100">
<span>Emails:</span>
<TextArea
className="mb-3 email-notes"
value={this.state.feature.attributes.email}
/>
</label>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
);
}
}