Not sure when this was implemented, but I noticed today that, at least on Date/Time/DateTime picker elementsā On Changed triggers, we now have name, event, value, and a few other properties available to pass along in the params object for event callbacks. This has already proven very useful, so I wanted to raise awareness with an example and say thank you for implementing it!
Quick rundown of my own use case.
Separate Date picker and Time picker with a shared js Date state object for the value where the pickers update their respective half of the shared Date according to the scope defined.
Quick aside - this would be an argument in favor for also providing a property that conveys the ātypeā of the calling element context from the definitionId or component name in the ui, so: ādateEditā/ātimeEditā/ādateTimeEditā or āDate pickerā/āTime pickerā/āDate & Time pickerā so that the changeScope can be inferred instead of hardcoded like below.
In any case, this allows for a structure thatās very close to what I had in mind when I made the feature request:
Component: timePicker
Trigger: On Change
Action: setSharedDateTime
Action Arguments:
{
// small wrapper so we don't have to pass a string or hardcode the action to use a specific state var
targetState: {
get: () => state.selectedDateTime,
set: val => {
state.selectedDateTime = val;
}
},
// small wrapper utilizing the new name property passed into the trigger context
// we could also forward name, value, event, valid, etc directly if that is useful
uiState: {
get: () => ui[name].value,
set: val => {
ui[name].value = val;
}
},
// defines what half of the shared js Date object should be updated by this event: Date, Time, or both
changeScope: 'time'
}
Action: setSharedDateTime
JS Code Step
const VALID_SCOPES = ['date', 'time', 'datetime'];
function isValidDate(value) {
return value instanceof Date && !Number.isNaN(value.getTime());
}
function isValidScope(value) {
return VALID_SCOPES.includes(value);
}
console.log('params:', params);
const targetState = params?.targetState;
const uiState = params?.uiState;
const scope = isValidScope(params?.changeScope) ? params.changeScope : null;
const incomingDate = uiState?.get?.();
const currentDate = targetState?.get?.();
if (!targetState?.get || !targetState?.set || !scope || !incomingDate) {
return isValidDate(currentDate) ? currentDate : null;
}
const current = isValidDate(currentDate) ? moment(currentDate) : moment();
const incoming = moment(incomingDate);
const next = current.clone();
if (scope === 'date' || scope === 'datetime') {
next.year(incoming.year())
.month(incoming.month())
.date(incoming.date());
}
if (scope === 'time' || scope === 'datetime') {
next.hour(incoming.hour())
.minute(incoming.minute())
.second(incoming.second())
.millisecond(incoming.millisecond());
}
const nextDate = next.toDate();
if (isValidDate(currentDate) && nextDate.getTime() === currentDate.getTime()) {
targetState.set(currentDate);
return currentDate;
}
targetState.set(nextDate);
return nextDate;
So now any number of date/time/datetime pickers can reference and update a shared js date object and the only thing that I need to change is what scope should be used for the update. The next step will potentially be to pull the shared parts of the Action Arguments params object out (the targetState and uiState bits) into a common space like a state var, js file, or something like that so I can update the params in one location and know that all components using that configuration will stay in sync.
Hopefully this helps illustrate what Iām referring to when I say āfunctionalā and ādata drivenā in requests and how it might be used. Iām certain my approach here will continue to evolve, so thereās no claim that the above example is the best approach etc, but I hope itās useful to someone regardless.