Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/survey-core/src/survey-events-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ export interface CompletingEvent extends CompleteBaseEvent {
* @deprecated Use `options.allow` instead.
*/
allowComplete: boolean;
message?: string;
}
export interface CompleteEvent extends CompleteBaseEvent {
/**
Expand Down
55 changes: 32 additions & 23 deletions packages/survey-core/src/survey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ export class SurveyModel extends SurveyElementCore
* @see doComplete
* @see autoAdvanceAllowComplete
*/
public onCompleting: EventBase<SurveyModel, CompletingEvent> = this.addEvent<SurveyModel, CompletingEvent>();
public onCompleting: EventAsync<SurveyModel, CompletingEvent> = this.addAsyncEvent<SurveyModel, CompletingEvent>();
/**
* An event that is raised after the survey is completed. Use this event to send survey results to the server.
*
Expand Down Expand Up @@ -4843,11 +4843,7 @@ export class SurveyModel extends SurveyElementCore
if (this.doServerValidation(doComplete)) return false;
if (doComplete) {
this.currentPage.passed = true;
const res = this.doComplete(this.canBeCompletedByTrigger, this.completedTrigger);
if (res) {
this.cancelPreview();
}
return res;
return this.doComplete(this.canBeCompletedByTrigger, this.completedTrigger);
}
this.doNextPage();
return true;
Expand Down Expand Up @@ -5311,20 +5307,23 @@ export class SurveyModel extends SurveyElementCore
* @param completeTrigger For internal use.
* @returns `false` if survey completion is cancelled within the [`onCompleting`](https://surveyjs.io/form-library/documentation/api-reference/survey-data-model#onCompleting) event handler; otherwise, `true`.
*/
public doComplete(isCompleteOnTrigger: boolean = false, completeTrigger?: Trigger): boolean {
public doComplete(isCompleteOnTrigger: boolean = false, completeTrigger?: Trigger): boolean | undefined {
if (this.isCompleted) return;
if (!this.checkOnCompletingEvent(isCompleteOnTrigger, completeTrigger)) {
this.isCompleted = false;
return false;
}
this.checkOnPageTriggers(true);
this.stopTimer();
this.notifyQuestionsOnHidingContent(this.currentPage);
this.isCompleted = true;
this.clearUnusedValues();
this.saveDataOnComplete(isCompleteOnTrigger, completeTrigger);
this.setCookie();
return true;

return this.checkOnCompletingEvent(isCompleteOnTrigger, completeTrigger, (allow) => {
if (allow) {
this.checkOnPageTriggers(true);
this.stopTimer();
this.notifyQuestionsOnHidingContent(this.currentPage);
this.isCompleted = true;
this.clearUnusedValues();
this.saveDataOnComplete(isCompleteOnTrigger, completeTrigger);
this.setCookie();
this.cancelPreview();
} else {
this.isCompleted = false;
}
});
}
private saveDataOnComplete(isCompleteOnTrigger: boolean = false, completeTrigger?: Trigger) {
let previousCookie = this.hasCookie;
Expand Down Expand Up @@ -5364,15 +5363,25 @@ export class SurveyModel extends SurveyElementCore
this.navigateTo();
}
}
private checkOnCompletingEvent(isCompleteOnTrigger: boolean, completeTrigger?: Trigger): boolean {
var options = {
private checkOnCompletingEvent(isCompleteOnTrigger: boolean, completeTrigger: Trigger, onComplete: (allow: boolean) => void): boolean | undefined {
let result: boolean | undefined = undefined;
const options: CompletingEvent = {
allowComplete: true,
allow: true,
isCompleteOnTrigger: isCompleteOnTrigger,
completeTrigger: completeTrigger
};
this.onCompleting.fire(this, options);
return options.allowComplete && options.allow;
const doCompleteFunc = () => {
this.isNavigationBlocked = false;
const allow = options.allowComplete && options.allow;
if (!!options.message) {
this.notify(options.message, allow ? "success" : "error");
}
result = allow;
onComplete(allow);
};
this.onCompleting.fire(this, options, doCompleteFunc, () => this.isNavigationBlocked = true);
return result;
}
/**
* Starts the survey. Applies only if the survey has a [start page](https://surveyjs.io/form-library/documentation/design-survey/create-a-multi-page-survey#start-page).
Expand Down
52 changes: 52 additions & 0 deletions packages/survey-core/tests/surveytests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3136,6 +3136,58 @@ QUnit.test("survey.onCurrentPageChanging async calls, Issue#10645", (assert) =>
}, 0);
});

QUnit.test("survey.onCompleting async calls, Issue#10645", (assert) => {
const done = assert.async();
const survey = new SurveyModel({
pages: [
{ name: "page1", elements: [{ type: "text", name: "q1" }] },
],
});

const messages: Array<any> = [];
survey.notify = (message: string, type: string) => {
messages.push({ message, type });
};
const completeAction = survey.navigationBar.getActionById("sv-nav-complete");
let allowComplete = false;
let messageToShow: string = "";
survey.onCompleting.add((survey, options): Promise<void> => {
return new Promise<void>((resolve) => {
options.allow = allowComplete;
options.message = messageToShow;
resolve();
});
});

messageToShow = "Test error on complete";
survey.tryComplete();
assert.equal(survey.state, "running", "Survey is not completed yet, we are waiting for a callback");
assert.equal(survey.getPropertyValue("isNavigationBlocked"), true, "isCurrentPageChanging = true, #1");
assert.equal(completeAction.enabled, false, "next action is disabled during navigation, #1");
setTimeout(() => {
assert.equal(survey.getPropertyValue("isNavigationBlocked"), false, "isCurrentPageChanging = false, #1");
assert.equal(survey.state, "running", "Do not allow complete");
assert.equal(completeAction.enabled, true, "next action is enabled after navigation, #1");

allowComplete = true;
messageToShow = "Test success on complete";
survey.tryComplete();
assert.equal(survey.getPropertyValue("isNavigationBlocked"), true, "isCurrentPageChanging = true, #2");
assert.equal(completeAction.enabled, false, "next action is disabled during navigation, #2");
assert.equal(survey.state, "running", "Do not allow complete #2");
setTimeout(() => {
assert.equal(survey.getPropertyValue("isNavigationBlocked"), false, "isCurrentPageChanging = false, #2");
assert.equal(completeAction.enabled, true, "next action is enabled after navigation, #2");
assert.equal(survey.state, "completed", "Survey is completed now");
assert.deepEqual(messages, [
{ message: "Test error on complete", type: "error" },
{ message: "Test success on complete", type: "success" }
]);
done();
}, 0);
}, 0);
});

QUnit.test("survey.onCurrentPageChanging, allow option (use it instead of allowChanging)", function (
assert
) {
Expand Down