I want to enable a custom widget to consume the "View in Table" data action provided by the Table widget, but I don't want to use the DataActionDropDown component. How do I trigger the action programmatically? I don't see any methods to do so in the DataActionManager class.
Solved! Go to Solution.
I recently went through a similar issue, and realized that state and props, aren't always the same when the widgets load, this lead to me looking into the Redux part of the React implementation in Experience builder. Did you find you had to use a similar solution?
Good morning dear,
How do I create a Data RecordSet (https://developers.arcgis.com/experience-builder/api-reference/jimu-core/DataRecordSet/) from a record that contains feature and dataSource to pass as a parameter to onExecute() ??
Can anybody help me? any example?
Thanks.
I know you were asking about this in march, but here's how I approached it:
In the action.ts file for the action (note whatever I named the action I'd name it like name-for-action.ts
(where named-for is a kebab case version of the action name.)
Here is a genericized version of an example of onExecute in that class:
onExecute (message: Message, actionConfig?: any): boolean | Promise<boolean> {
if (message.type === MessageType.DataRecordsSelectionChange) {
const dataRecordSetChangeMessage = message as DataRecordSetChangeMessage
const widgetId = dataRecordSetChangeMessage.widgetId
console.log('Received data recordSetChangeMessage:', dataRecordSetChangeMessage)
// @ts-expect-error
const records = dataRecordSetChangeMessage.records || []
if (records.length > 0) {
const selectedRecord = records[0]
const selectedFeature = selectedRecord?.feature
if (selectedFeature) {
const selectedAttributes = {
attributes: selectedFeature.attributes,
// (Note attribute_value_of_note in the system I was using could be lowercase or all upper case depending on whether it is a hosted layer or not) It is a generic name and could be replaced with any field you'd find in the feature.attriubtes that are returned.
attribute_value_of_note: selectedFeature.attributes.ATTRIBUTE_VALUE_OF_NOTE || selectedFeature.attributes.attribute_value_of_note,
feature: selectedFeature,
geometry: selectedFeature.geometry
}
console.log('Dispatching selected attribute_value_of_note attributes:', selectedAttributes)
// Dispatch actions to the new Redux store extension
getAppStore().dispatch(setSelectedAttributeOfValue(selectedAttributes.attribute_value_of_note))
getAppStore().dispatch(setSelectedAttributes(selectedAttributes.attributes))
// Convert geometry to JSON before dispatching
getAppStore().dispatch(setSelectedGeometry(selectedFeature.geometry))
getAppStore().dispatch(setSelectedFeature(selectedFeature))
// If needed, you can still poll for state updates in the new store
this.pollForSelectedAttributeOfValue(widgetId)
.then(() => {
console.log('selectedAttributeOfValue successfully stored in the Redux store.')
})
.catch((error) => {
console.error(error.message)
})
} else {
console.error('No feature found in the selected record.')
return false
}
} else {
console.error('No records found in the message.')
return false
}
} else {
console.error('Message type does not match.')
return false
}
return true
}
action-types.ts
// action-types.ts
export enum YourActionKeys {
SetSelectedAttributeOfNote = 'SET_SELECTED_ATTRIBUTE_OF_NOTE',
SetSelectedAttributes = 'SET_SELECTED_ATTRIBUTES',
SetSelectedGeometry = 'SET_SELECTED_GEOMETRY',
SetSelectedFeature = 'SET_SELECTED_FEATURE'
}
actions.ts
// actions.ts
import { YourActionKeys } from './action-types'
export const setSelectedAttributeOfNote = (attributeOfNote: string) => ({
type: YourActionKeys.SetSelectedAttributeOfNote,
attributeOfNote
})
export const setSelectedAttributes = (attributes: any) => ({
type: YourActionKeys.SetSelectedAttributes,
attributes
})
export const setSelectedGeometry = (geometry: __esri.Geometry) => {
const geometryJSON = geometry.toJSON() // Convert to plain JSON object
return {
type: YourActionKeys.SetSelectedGeometry,
geometry: geometryJSON
}
}
export const setSelectedFeature = (feature: __esri.Geometry) => {
return {
// Note instead of setting up an Enum you could actually just have the string like this)
type: 'SET_SELECTED_FEATURE',
payload: feature
}
}
then in the store file for your widget. you need a getReducer method (but some of the interface and setup for the store can be defined here too:
// my-store.ts
import { type extensionSpec, type ImmutableObject, type IMState } from 'jimu-core'
import { YourActionKeys } from './action-types'
// interface MyState {
// selectedAttributeOfNote: string | null
// selectedAttributes: any
// }
// Note I used any because its often quicker than trying to speck out the deeply nested structures that might be returned. If you want more type checking, definitely choose something other than any.
export interface YourWidgetStoreState {
selectedAttributeOfNote: string | null
selectedAttributes: any
selectedGeometry: __esri.Geometry | null
selectedFeature: any
}
export interface StateForYourWidgetStore extends IMState {
YourWidgetStoreState: ImmutableObject<YourWidgetStoreState>
}
type IMYourWidgetStoreState = ImmutableObject<YourWidgetStoreState>
export default class YouWidgetStoreExtension implements extensionSpec.ReduxStoreExtension {
id = 'your-results-store-extension'
getActions () {
return Object.keys(YourActionKeys).map(k => YourActionKeys[k])
}
getInitLocalState () {
return {
selectedAttributeOfNote: null,
selectedAttributes: null,
selectedFeature: null,
selectedGeometry: null
}
}
getReducer () {
return (localState: IMYourWidgetStoreState, action: any, appState: IMState): IMYourWidgetStoreState => {
switch (action.type) {
case YourActionKeys.SetSelectedAttributeOfNote:
return localState.set('selectedAttributeOfNote', action.attribute_of_note)
case YourActionKeys.SetSelectedAttributes:
return localState.set('selectedAttributes', action.attributes)
case YourActionKeys.SetSelectedGeometry:
return localState.set('selectedGeometry', action.geometry)
case YourActionKeys.SetSelectedFeature:
return localState.set('selectedFeature', action.feature)
default:
return localState
}
}
}
getStoreKey () {
return 'YourWidgetStoreState'
}
}
In theory you can generically then get access to what you stored when the action is triggered by just doing this:
const store = getAppStore()
const storeState = store.getState() as StateForYourWidgetStore
// Access yourWidgetStoreState directly from the store
const yourWidgetStoreState = storeState.yourWidgetStoreState
console.log('storeState.yourWidgetStoreState: ', yourWidgetStoreState)
if (yourWidgetStoreState && yourWidgetStoreState.selectedAttributeOfNote) {
console.log(`Detected existing yourWidgetStoreState with attribute of note: ${yourWidgetStoreState.selectedAttributeOfNote}`)
} else {
console.log('No existing yourWidgetStoreState or attribute of note found.')
}
In this example I was able to check this when the widget opened at construction, but in theory you could do it in whatever function in your widget would receive that message call.