Select to view content in your preferred language

Release the onClick Event when widget is closed/collapsed

1329
9
09-18-2023 06:50 AM
PedroSalazar01
New Contributor II

Can someone guide me on how to release the onClick event? we have this setup in webapp builder but have not found the way to do it in Experience builder, see code

see snippet code web app builder

onClose: function(){
var mapFrame = this;
var map = this.map;
var my_content = document.getElementById("MyGoogleWidget");
var container = document.getElementById("btn_holder");

map.graphics.clear();
mapClick.remove();

mapFrame.LatTextBox.value = "";
mapFrame.LongTextBox.value = "";
container.innerHTML = "";
my_content.classList.add("hidden");
}

 

Experience Builder

import { React, type AllWidgetProps, type Color } from 'jimu-core'
import { JimuMapViewComponent, type JimuMapView } from 'jimu-arcgis'
import type Point from 'esri/geometry/Point'
import Graphic from 'esri/Graphic'

const { useState } = React

const linkButton = {
  display: 'block',
  color: '#f2f2f2',
  backgroundColor: '#3C73BD',
  fontWeight: 'bold',
  padding: '12px 15px',
  fontFamily: 'Sans-Serif',
  borderRadius: '50px',
  textAlign: 'center',
  textDecoration: 'none'

}

// interface State {
//   jimuMapView: JimuMapView
// }

const Widget = (props: AllWidgetProps<any>) => {
  const [latitude, setLatitude] = useState<string>('')
  const [longitude, setLongitude] = useState<string>('')
  const mainURL = url + latitude + ' ' + longitude
  const [visible, setVisible] = React.useState(false)
  // const [graphic, setGraphic] = useState(null);
  const activeViewChangeHandler = (jmv: JimuMapView) => {
    // if (this.state.jimuMapView) {
    //   if (this.state.currentWidget) {
    //     this.state.currentWidget.destroy()
    //   }
    // }
    if (jmv) {
      // When the pointer moves, take the pointer location and create a Point
      // Geometry out of it (`view.toMap(...)`), then update the state.

      const simpleMarkerSymbol = {
        type: 'simple-marker',
        color: [226, 119, 40], // Orange
        outline: {
          color: [255, 255, 255], // White
          width: 1
        }
      }
      
      jmv.view.on('click', evt => {
        const point: Point = jmv.view.toMap({
          x: evt.x,
          y: evt.y
        })
        setLatitude(point.latitude.toFixed(8))
        setLongitude(point.longitude.toFixed(8))
        setVisible(true)
        const graphic = new Graphic({
          geometry: point,
          symbol: simpleMarkerSymbol
        })
        // setGraphic(graphic);
        jmv.view.graphics.removeAll()
        jmv.view.graphics.add(graphic)
      })
    }
  }

  return (

    <div className="widget-starter jimu-widget">
      {
        props.useMapWidgetIds &&
        props.useMapWidgetIds.length === 1 && (
          <JimuMapViewComponent
            useMapWidgetId={props.useMapWidgetIds?.[0]}
            onActiveViewChange={activeViewChangeHandler}

          />
        )
      }

      <p><h9>Click on Map to get the coordinates </h9></p>

      <div style={ { display: visible ? 'block' : 'none' } }>
        <div>
        <p style={{ textAlign: 'center' }}><strong>Lat/Lon: {latitude} {longitude}</strong> </p>
        {/* <p style={myStyle}><button onClick={ () => openLinkInNewTab(mainURL)}>View in Google Maps!</button></p> */}
        <a style={linkButton} href={mainURL} target='_blank'>View in Google Maps</a>
       </div>
      </div>

 </div>

  )
}

export default Widget

 

0 Kudos
9 Replies
KenBuja
MVP Esteemed Contributor

The view's on method returns a handler that has a remove method. In this example, the click handler is removed after the fifth click.

let counter = 1;
const event = view.on("click", evt => {
  console.log("click " + counter);
  if (counter > 4) event.remove();
  counter++;
});
0 Kudos
PedroSalazar01
New Contributor II

Ken:

How do you reinitialize the event once its been removed without refreshing the map?

right now i have to refresh the map, I would want it reinitialized when the widget its open again.

Thanks in advance

Pedro

0 Kudos
JeffreyThompson2
MVP Regular Contributor

If I understand your question, you have a widget in a Widget Controller and you want to turn a click event on and off when the widget is activated. I have done this before in a class based component, but not a functional one, so my code may be slightly off, but I would handle this through a useEffect function triggered by the WidgetState. (I believe this can be found in props.state.)

const eventListener = useRef(null)
useEffect(()=> {
   if (props.state === 'OPENED') {
      eventListener.current = view.on('click', evt => {
        yourFunction()
      } 
   } else if (props.state === 'CLOSED') {
     eventListener.current.remove()
   }
},[props.state])

 

GIS Developer
City of Arlington, Texas
0 Kudos
PedroSalazar01
New Contributor II
Any ideas why this is not working? when I debug , the program does not get the activeViewChangeHandler.
it does go to the alerts

does any thing stands out?
thanks in advance



const Widget = (props: AllWidgetProps) => {
const { useState, useEffect } = React
const eventListener = useRef(null)
const [latitude, setLatitude] = useState('')
const [longitude, setLongitude] = useState('')
const url = 'https://www.google.com/maps/search/?api=1&query='
const mainURL = url + latitude + ' ' + longitude
const [visible, setVisible] = React.useState(false)

useEffect(() => {
if (props.state === 'OPENED') {
alert('open')
activeViewChangeHandler = (jmv: JimuMapView) => {
if (jmv) {
const simpleMarkerSymbol = {
type: 'simple-marker',
color: [226, 119, 40], // Orange
outline: {
color: [255, 255, 255], // White
width: 1
}
}
eventListener.current = jmv.view.on('click', evt => {
const point: Point = jmv.view.toMap({
x: evt.x,
y: evt.y
})
setLatitude(point.latitude.toFixed(8))
setLongitude(point.longitude.toFixed(8))
setVisible(true)
const graphic = new Graphic({
geometry: point,
symbol: simpleMarkerSymbol
})
// setGraphic(graphic);
jmv.view.graphics.removeAll()
jmv.view.graphics.add(graphic)
})
}
}
} else if (props.state === 'CLOSED') {
//eventListener.current.remove()
alert('closed')
}
//}, [props.state])
}, [props.state])
// const openLinkInNewTab = (url) => {
// const newTab = window.open(url, '_blank', 'noopener,noreferrer')
// if (newTab) newTab.opener = null
// }

return (


{
props.useMapWidgetIds &&
props.useMapWidgetIds.length === 1 && (

useMapWidgetId={props.useMapWidgetIds?.[0]}
onActiveViewChange={activeViewChangeHandler}

/>
)
}

0 Kudos
JeffreyThompson2
MVP Regular Contributor

This is my base code for a widget that will need to interact with a map. It assumes that there is only one map in the Experience. It looks like you are missing a call to JimuMapViewComponent. I can also see some syntax problems in your return statement.

import { React } from 'jimu-core'
import { MapViewManager, JimuMapView, JimuMapViewComponent } from 'jimu-arcgis'
import reactiveUtils from 'esri/core/reactiveUtils'

const { useEffect, useState } = React

const Widget = (props) => {
    const viewManager = MapViewManager.getInstance()
    const mapView = viewManager.getJimuMapViewById(viewManager.getAllJimuMapViewIds()[0])
	const [jimuMapView, setJimuMapView] = useState<JimuMapView>(mapView)
	const [mapReady, setMapReady] = useState(false)

	useEffect(() => {
		if (jimuMapView) {
			reactiveUtils
				.whenOnce(() => jimuMapView.view.ready)
				.then(() => {
					setMapReady(true)
				}
				)
		}

	}, [jimuMapView])

	const activeViewChangeHandler = (jmv: JimuMapView) => {
		if (jmv) {
			setJimuMapView(jmv)
		}
	}

    return (
		<div className='jimu-widget'
			{
				...
				props.useMapWidgetIds &&
				props.useMapWidgetIds.length === 1 && (
					<JimuMapViewComponent
						useMapWidgetId={props.useMapWidgetIds?.[0]}
						onActiveViewChange={activeViewChangeHandler}
					/>
				)
			}
		>
			{mapReady ? 'map ready' : 'map not ready'}
		</div>
    )
}

export default Widget

 

GIS Developer
City of Arlington, Texas
0 Kudos
PedroSalazar01
New Contributor II
useEffect(() => {
    if (jimuMapView) {
      reactiveUtils
        .whenOnce(() => jimuMapView.view.ready)
        .then(() => {
          setMapReady(true)
          alert('map ready')
          if (props.state === 'OPENED') {
            if (!eventListener) {
              alert('done')
              eventListener = eventListener2.current
              eventListener.current = jmv2.view.on('click', evt => {})
            }
            const test: string = eventListener.current.Status
            alert(test)

            alert('open')
          } else if (props.state === 'CLOSED') {
            alert('closed')
            eventListener.current.remove()
            alert(eventListener)
            jmv2.view.graphics.removeAll()
          }
        }
        )
    }
  }, [jimuMapView, props.state])
  const activeViewChangeHandler = (jmv: JimuMapView) => {
    jmv2 = jmv
    if (jmv) {
      // When the pointer moves, take the pointer location and create a Point
      // Geometry out of it (`view.toMap(...)`), then update the state.

      const simpleMarkerSymbol = {
        type: 'simple-marker',
        color: [226, 119, 40], // Orange
        outline: {
          color: [255, 255, 255], // White
          width: 1
        }
      }
      //jmv.view.on('pointer-move', evt => {
      eventListener.current = jmv.view.on('click', evt => {
        alert('Point')
        eventListener2 = eventListener
        const point: Point = jmv.view.toMap({
          x: evt.x,
          y: evt.y
        })
        setLatitude(point.latitude.toFixed(8))
        setLongitude(point.longitude.toFixed(8))
        setVisible(true)
        const graphic = new Graphic({
          geometry: point,
          symbol: simpleMarkerSymbol
        })
        // setGraphic(graphic);
        jmv.view.graphics.removeAll()
        jmv.view.graphics.add(graphic)
        //alert("Hello")
        //jmv.view.add(graphic);
        //const graphicsLayer = new GraphicsLayer();
        // jmv.view.Graphics.add(graphicsLayer)
        //  graphicsLayer.add(graphic)
      })
      // Set up a click event handler and retrieve the screen point
    }
0 Kudos
PedroSalazar01
New Contributor II

jeffery:

Tried all kind of ways to re-add the listner

EvnetListner

But once i remove it when closing the widget

eventListener.current.remove()

i cannot re add it

any help will be greatly appreciated

Thanks

 

0 Kudos
JeffreyThompson2
MVP Regular Contributor

I suggest stepping back from Experience Builder and studying up on React before proceeding further. https://react.dev/learn React operates very differently from plain JavaScript and has a very steep learning curve.  And Experience Builder adds another layer of complexity on top of that. I can tell you that there are issues with how you are using the useRef hook that are causing at least some of your problems. https://react.dev/reference/react/useRef Here is some pseudo-code for how to make this widget.

//import statements
//widget declaration
//useState declarations
//const eventListener = useRef(null)
//Declare a createEventListener function that returns the eventListener.
//useEffect function that has props.state in its depenency array.
//Inside useEffect, an if/else if statement based on props.state.
//If OPENED and if !eventListener.current, eventListener.current = createEventListener()
//Else if CLOSED, eventListener.current.remove() and eventListener.current = null
//return function
//export statement
GIS Developer
City of Arlington, Texas
0 Kudos
PedroSalazar01
New Contributor II
Thanks for the advise,
I will do just that
have a great day
0 Kudos