Skip to content

Commit 8d0eaa9

Browse files
authored
Merge pull request #1443 from lowcoder-org/feat/query_triggers
Feat/query triggers
2 parents 4ced9ee + 9ce2172 commit 8d0eaa9

File tree

3 files changed

+155
-29
lines changed

3 files changed

+155
-29
lines changed

client/packages/lowcoder/src/comps/queries/queryComp.tsx

Lines changed: 81 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -97,10 +97,25 @@ interface AfterExecuteQueryAction {
9797
result: QueryResult;
9898
}
9999

100-
const TriggerTypeOptions = [
100+
const CommonTriggerOptions = [
101+
{ label: trans("query.triggerTypeInputChange"), value: "onInputChange"},
102+
{ label: trans("query.triggerTypeQueryExec"), value: "onQueryExecution"},
103+
{ label: trans("query.triggerTypeTimeout"), value: "onTimeout"},
104+
]
105+
106+
export const TriggerTypeOptions = [
107+
{ label: trans("query.triggerTypePageLoad"), value: "onPageLoad"},
108+
...CommonTriggerOptions,
101109
{ label: trans("query.triggerTypeAuto"), value: "automatic" },
102110
{ label: trans("query.triggerTypeManual"), value: "manual" },
103111
] as const;
112+
113+
export const JSTriggerTypeOptions = [
114+
...CommonTriggerOptions,
115+
{ label: trans("query.triggerTypePageLoad"), value: "automatic" },
116+
{ label: trans("query.triggerTypeManual"), value: "manual" },
117+
];
118+
104119
export type TriggerType = ValueFromOption<typeof TriggerTypeOptions>;
105120

106121
const EventOptions = [
@@ -151,6 +166,13 @@ const childrenMap = {
151166
},
152167
}),
153168
cancelPrevious: withDefault(BoolPureControl, false),
169+
// use only for onQueryExecution trigger
170+
depQueryName: SimpleNameComp,
171+
// use only for onTimeout trigger, triggers query after x time passed on page load
172+
delayTime: millisecondsControl({
173+
left: 0,
174+
defaultValue: 5 * 1000,
175+
})
154176
};
155177

156178
let QueryCompTmp = withTypeAndChildren<typeof QueryMap, ToInstanceType<typeof childrenMap>>(
@@ -174,6 +196,7 @@ export type QueryChildrenType = InstanceType<typeof QueryCompTmp> extends MultiB
174196
? X
175197
: never;
176198

199+
let blockInputChangeQueries = true;
177200
/**
178201
* Logic to automatically trigger execution
179202
*/
@@ -222,11 +245,17 @@ QueryCompTmp = class extends QueryCompTmp {
222245
const isJsQuery = this.children.compType.getView() === "js";
223246
const notExecuted = this.children.lastQueryStartTime.getView() === -1;
224247
const isAutomatic = getTriggerType(this) === "automatic";
248+
const isPageLoadTrigger = getTriggerType(this) === "onPageLoad";
249+
const isInputChangeTrigger = getTriggerType(this) === "onInputChange";
225250

226251
if (
227-
action.type === CompActionTypes.UPDATE_NODES_V2 &&
228-
isAutomatic &&
229-
(!isJsQuery || (isJsQuery && notExecuted)) // query which has deps can be executed on page load(first time)
252+
action.type === CompActionTypes.UPDATE_NODES_V2
253+
&& (
254+
isAutomatic
255+
|| isInputChangeTrigger
256+
|| (isPageLoadTrigger && notExecuted)
257+
)
258+
// && (!isJsQuery || (isJsQuery && notExecuted)) // query which has deps can be executed on page load(first time)
230259
) {
231260
const next = super.reduce(action);
232261
const depends = this.children.comp.node()?.dependValues();
@@ -250,6 +279,18 @@ QueryCompTmp = class extends QueryCompTmp {
250279
const dependsChanged = !_.isEqual(preDepends, depends);
251280
const dslNotChanged = _.isEqual(preDsl, dsl);
252281

282+
if(isInputChangeTrigger && blockInputChangeQueries && dependsChanged) {
283+
// block executing input change queries initially on page refresh
284+
setTimeout(() => {
285+
blockInputChangeQueries = false;
286+
}, 500)
287+
288+
return setFieldsNoTypeCheck(next, {
289+
[lastDependsKey]: depends,
290+
[lastDslKey]: dsl,
291+
});
292+
}
293+
253294
// If the dsl has not changed, but the dependent node value has changed, then trigger the query execution
254295
// FIXME, this should be changed to a reference judgement, but for unknown reasons if the reference is modified once, it will change twice.
255296
if (dependsChanged) {
@@ -277,24 +318,33 @@ function QueryView(props: QueryViewProps) {
277318
useEffect(() => {
278319
// Automatically load when page load
279320
if (
280-
getTriggerType(comp) === "automatic" &&
321+
(
322+
getTriggerType(comp) === "automatic"
323+
|| getTriggerType(comp) === "onPageLoad"
324+
) &&
281325
(comp as any).isDepReady &&
282326
!comp.children.isNewCreate.value
283327
) {
284328
setTimeout(() => {
285329
comp.dispatch(deferAction(executeQueryAction({})));
286330
}, 300);
287331
}
332+
333+
if(getTriggerType(comp) === "onTimeout") {
334+
setTimeout(() => {
335+
comp.dispatch(deferAction(executeQueryAction({})));
336+
}, comp.children.delayTime.getView());
337+
}
288338
}, []);
289339

290340
useFixedDelay(
291341
() =>
292342
getPromiseAfterDispatch(comp.dispatch, executeQueryAction({}), {
293343
notHandledError: trans("query.fixedDelayError"),
294344
}),
295-
getTriggerType(comp) === "automatic" && comp.children.periodic.getView()
296-
? comp.children.periodicTime.getView()
297-
: null
345+
getTriggerType(comp) === "automatic" && comp.children.periodic.getView()
346+
? comp.children.periodicTime.getView()
347+
: null
298348
);
299349

300350
return null;
@@ -609,6 +659,29 @@ export const QueryComp = withExposingConfigs(QueryCompTmp, [
609659
const QueryListTmpComp = list(QueryComp);
610660

611661
class QueryListComp extends QueryListTmpComp implements BottomResListComp {
662+
override reduce(action: CompAction): this {
663+
if (isCustomAction<AfterExecuteQueryAction>(action, "afterExecQuery")) {
664+
if (action.path?.length === 1 && !isNaN(parseInt(action.path[0]))) {
665+
const queryIdx = parseInt(action.path[0]);
666+
const queryComps = this.getView();
667+
const queryName = queryComps?.[queryIdx]?.children.name.getView();
668+
const dependentQueries = queryComps.filter((query, idx) => {
669+
if (queryIdx === idx) return false;
670+
if (
671+
getTriggerType(query) === 'onQueryExecution'
672+
&& query.children.depQueryName.getView() === queryName
673+
) {
674+
return true;
675+
}
676+
})
677+
dependentQueries?.forEach((query) => {
678+
query.dispatch(deferAction(executeQueryAction({})));
679+
})
680+
}
681+
}
682+
return super.reduce(action);
683+
}
684+
612685
nameAndExposingInfo(): NameAndExposingInfo {
613686
const result: NameAndExposingInfo = {};
614687
Object.values(this.children).forEach((comp) => {

client/packages/lowcoder/src/comps/queries/queryComp/queryPropertyView.tsx

Lines changed: 70 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import { useSelector } from "react-redux";
2626
import { getDataSource, getDataSourceTypes } from "redux/selectors/datasourceSelectors";
2727
import { BottomResTypeEnum } from "types/bottomRes";
2828
import { EditorContext } from "../../editorState";
29-
import { QueryComp } from "../queryComp";
29+
import { JSTriggerTypeOptions, QueryComp, TriggerType, TriggerTypeOptions } from "../queryComp";
3030
import { ResourceDropdown } from "../resourceDropdown";
3131
import { NOT_SUPPORT_GUI_SQL_QUERY, SQLQuery } from "../sqlQuery/SQLQuery";
3232
import { StreamQuery } from "../httpQuery/streamQuery";
@@ -37,6 +37,7 @@ import styled from "styled-components";
3737
import { DataSourceButton } from "pages/datasource/pluginPanel";
3838
import { Tooltip, Divider } from "antd";
3939
import { uiCompRegistry } from "comps/uiCompRegistry";
40+
import { InputTypeEnum } from "@lowcoder-ee/comps/comps/moduleContainerComp/ioComp/inputListItemComp";
4041

4142
const Wrapper = styled.div`
4243
width: 100%;
@@ -226,6 +227,42 @@ export const QueryGeneralPropertyView = (props: {
226227
comp.children.datasourceId.dispatchChangeValueAction(QUICK_REST_API_ID);
227228
}
228229

230+
const triggerOptions = useMemo(() => {
231+
if (datasourceType === "js" || datasourceType === "streamApi") {
232+
return JSTriggerTypeOptions;
233+
}
234+
return TriggerTypeOptions;
235+
}, [datasourceType]);
236+
237+
const getQueryOptions = useMemo(() => {
238+
const options: { label: string; value: string }[] =
239+
editorState
240+
?.queryCompInfoList()
241+
.map((info) => ({
242+
label: info.name,
243+
value: info.name,
244+
}))
245+
.filter((option) => {
246+
// Filter out the current query under query
247+
if (editorState.selectedBottomResType === BottomResTypeEnum.Query) {
248+
return option.value !== editorState.selectedBottomResName;
249+
}
250+
return true;
251+
}) || [];
252+
253+
// input queries
254+
editorState
255+
?.getModuleLayoutComp()
256+
?.getInputs()
257+
.forEach((i) => {
258+
const { name, type } = i.getView();
259+
if (type === InputTypeEnum.Query) {
260+
options.push({ label: name, value: name });
261+
}
262+
});
263+
return options;
264+
}, [editorState]);
265+
229266
return (
230267
<QueryPropertyViewWrapper>
231268
<QuerySectionWrapper>
@@ -329,26 +366,38 @@ export const QueryGeneralPropertyView = (props: {
329366
</QueryConfigWrapper>
330367

331368
{placement === "editor" && (
332-
<TriggerTypeStyled>
333-
<Dropdown
334-
placement={"bottom"}
335-
label={trans("query.triggerType")}
336-
options={
337-
[
338-
{
339-
label:
340-
(children.compType.getView() === "js" || children.compType.getView() === "streamApi")
341-
? trans("query.triggerTypePageLoad")
342-
: trans("query.triggerTypeAuto"),
343-
value: "automatic",
344-
},
345-
{ label: trans("query.triggerTypeManual"), value: "manual" },
346-
] as const
347-
}
348-
value={children.triggerType.getView()}
349-
onChange={(value) => children.triggerType.dispatchChangeValueAction(value)}
350-
/>
351-
</TriggerTypeStyled>
369+
<>
370+
<TriggerTypeStyled>
371+
<Dropdown
372+
placement={"bottom"}
373+
label={trans("query.triggerType")}
374+
options={triggerOptions}
375+
value={children.triggerType.getView()}
376+
onChange={(value) => children.triggerType.dispatchChangeValueAction(value as TriggerType)}
377+
/>
378+
</TriggerTypeStyled>
379+
{children.triggerType.getView() === 'onQueryExecution' && (
380+
<TriggerTypeStyled>
381+
<Dropdown
382+
showSearch={true}
383+
placement={"bottom"}
384+
value={children.depQueryName.getView()}
385+
options={getQueryOptions}
386+
label={trans("eventHandler.selectQuery")}
387+
onChange={(value) => children.depQueryName.dispatchChangeValueAction(value)}
388+
/>
389+
</TriggerTypeStyled>
390+
)}
391+
{children.triggerType.getView() === 'onTimeout' && (
392+
<TriggerTypeStyled>
393+
{children.delayTime.propertyView({
394+
label: trans("query.delayTime"),
395+
placeholder: "5s",
396+
placement: "bottom",
397+
})}
398+
</TriggerTypeStyled>
399+
)}
400+
</>
352401
)}
353402
</QuerySectionWrapper>
354403

client/packages/lowcoder/src/i18n/locales/en.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -738,6 +738,10 @@ export const en = {
738738
"triggerTypeAuto": "Inputs Change or On Page Load",
739739
"triggerTypePageLoad": "When the Application (Page) loads",
740740
"triggerTypeManual": "Only when you trigger it manually",
741+
"triggerTypeInputChange": "When Inputs Change",
742+
"triggerTypeQueryExec": "After Query Execution",
743+
"triggerTypeTimeout": "After the Timeout Interval",
744+
"delayTime": "Delay Time",
741745
"chooseDataSource": "Choose Data Source",
742746
"method": "Method",
743747
"updateExceptionDataSourceTitle": "Update Failing Data Source",

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy