From 72af37ee225dbe44d5b22658e3f3acac5dc84759 Mon Sep 17 00:00:00 2001 From: Dmitry Kuzin Date: Mon, 26 Jun 2023 21:02:59 +0400 Subject: [PATCH] Work for https://github.com/surveyjs/survey-pdf/issues/251: fix isReadyFlag behaviour for onGetChoiceDisplayValue event --- src/question.ts | 11 +++++ src/question_baseselect.ts | 25 ++++++----- src/question_file.ts | 8 +--- src/question_paneldynamic.ts | 10 +---- tests/questionDropdownTests.ts | 81 ++++++++++++++++++++++++++++++++++ 5 files changed, 108 insertions(+), 27 deletions(-) diff --git a/src/question.ts b/src/question.ts index 4ba2de61ca..22ba49ac9e 100644 --- a/src/question.ts +++ b/src/question.ts @@ -209,6 +209,17 @@ export class Question extends SurveyElement this.valueName ? this.valueName : oldValue ); } + public set isReady(val: boolean) { + const oldIsReady = this.isReadyValue; + this.isReadyValue = val; + if(oldIsReady != val) { + this.onReadyChanged.fire(this, { + question: this, + isReady: val, + oldIsReady: oldIsReady, + }); + } + } public get isReady(): boolean { return this.isReadyValue; } diff --git a/src/question_baseselect.ts b/src/question_baseselect.ts index 2376d8adb5..f1ace9c09f 100644 --- a/src/question_baseselect.ts +++ b/src/question_baseselect.ts @@ -34,6 +34,12 @@ export class QuestionSelectBase extends Question { private newItemValue: ItemValue; private canShowOptionItemCallback: (item: ItemValue) => boolean; private waitingGetChoiceDisplayValueResponse: boolean; + private get waitingChoicesByURL(): boolean { + return !this.isChoicesLoaded && !this.choicesByUrl.isEmpty; + } + private get waitingAcyncOperations(): boolean { + return this.waitingChoicesByURL || this.waitingGetChoiceDisplayValueResponse; + } @property({ onSet: (newVal: any, target: QuestionSelectBase) => { target.onSelectedItemValuesChangedHandler(newVal); } }) protected selectedItemValues: any; @@ -208,7 +214,7 @@ export class QuestionSelectBase extends Question { return this.getLocalizableString("noneText"); } /** - * A Boolean expression that is evaluated against each choice item. If the expression evaluates to `false`, the choice item becomes hidden. + * A Boolean expression that is evaluated against each choice fitem. If the expression evaluates to `false`, the choice item becomes hidden. * * A survey parses and runs all expressions on startup. If any values used in the expression change, the survey re-evaluates it. * @@ -333,7 +339,7 @@ export class QuestionSelectBase extends Question { this.survey?.loadedChoicesFromServer(this); } protected getItemIfChoicesNotContainThisValue(value: any, text?: string): any { - if(!this.isReady) { + if(this.waitingChoicesByURL) { return this.createItemValue(value, text); } else { return null; @@ -573,6 +579,7 @@ export class QuestionSelectBase extends Question { protected updateSingleSelectedItemValues(): void { if (!!this.survey && !this.isEmpty() && !ItemValue.getItemByValue(this.choices, this.value)) { this.waitingGetChoiceDisplayValueResponse = true; + this.isReady = !this.waitingAcyncOperations; this.survey.getChoiceDisplayValue({ question: this, values: [this.value], @@ -580,6 +587,7 @@ export class QuestionSelectBase extends Question { this.waitingGetChoiceDisplayValueResponse = false; if (!displayValues || !displayValues.length) return; this.selectedItemValues = this.createItemValue(this.value, displayValues[0]); + this.isReady = !this.waitingAcyncOperations; } }); } @@ -590,6 +598,7 @@ export class QuestionSelectBase extends Question { if (!!this.survey && !this.isEmpty() && hasItemWithValues) { this.waitingGetChoiceDisplayValueResponse = true; + this.isReady = this.waitingAcyncOperations; this.survey.getChoiceDisplayValue({ question: this, values: valueArray, @@ -597,6 +606,7 @@ export class QuestionSelectBase extends Question { this.waitingGetChoiceDisplayValueResponse = false; if (!displayValues || !displayValues.length) return; this.selectedItemValues = displayValues.map((displayValue, index) => this.createItemValue(this.value[index], displayValue)); + this.isReady = !this.waitingAcyncOperations; } }); } @@ -1189,7 +1199,7 @@ export class QuestionSelectBase extends Question { : this.textProcessor; if (!processor) processor = this.survey; if (!processor) return; - this.isReadyValue = this.isChoicesLoaded || this.choicesByUrl.isEmpty; + this.isReadyValue = !this.waitingAcyncOperations; this.isRunningChoices = true; this.choicesByUrl.run(processor); this.isRunningChoices = false; @@ -1561,14 +1571,7 @@ export class QuestionSelectBase extends Question { public choicesLoaded(): void { this.isChoicesLoaded = true; - let oldIsReady: boolean = this.isReadyValue; - this.isReadyValue = true; - this.onReadyChanged && - this.onReadyChanged.fire(this, { - question: this, - isReady: true, - oldIsReady: oldIsReady, - }); + this.isReady = !this.waitingAcyncOperations; if (this.survey) { this.survey.loadedChoicesFromServer(this); } diff --git a/src/question_file.ts b/src/question_file.ts index a50e9d660b..bfee5f1417 100644 --- a/src/question_file.ts +++ b/src/question_file.ts @@ -369,13 +369,7 @@ export class QuestionFileModel extends Question { this.previewValue.push(val); }); } - this.isReadyValue = true; - this.onReadyChanged && - this.onReadyChanged.fire(this, { - question: this, - isReady: true, - oldIsReady: false, - }); + this.isReady = true; this._previewLoader.dispose(); this._previewLoader = undefined; }); diff --git a/src/question_paneldynamic.ts b/src/question_paneldynamic.ts index e20f896167..d0d88f53f3 100644 --- a/src/question_paneldynamic.ts +++ b/src/question_paneldynamic.ts @@ -1834,7 +1834,6 @@ export class QuestionPanelDynamicModel extends Question this.recalculateIsReadyValue(); }; recalculateIsReadyValue(): void { - let oldIsReady = this.isReadyValue; let isReady: boolean = true; this.panels.forEach(panel => { panel.questions.forEach(q => { @@ -1846,14 +1845,7 @@ export class QuestionPanelDynamicModel extends Question } }); }); - this.isReadyValue = isReady; - if(oldIsReady != this.isReadyValue) { - this.onReadyChanged.fire(this, { - question: this, - oldIsReady: oldIsReady, - isReady: this.isReadyValue - }); - } + this.isReady = isReady; } protected onSetData() { super.onSetData(); diff --git a/tests/questionDropdownTests.ts b/tests/questionDropdownTests.ts index 3396bd65d0..27b3a98fd0 100644 --- a/tests/questionDropdownTests.ts +++ b/tests/questionDropdownTests.ts @@ -1538,4 +1538,85 @@ QUnit.test("lazy loading placeholder", assert => { done(); }, onChoicesLazyLoadCallbackTimeOut + callbackTimeOutDelta); +}); + +QUnit.test("isReady flag + onGetChoiceDisplayValue", assert => { + const done = assert.async(); + + const json = { + questions: [{ + "type": "dropdown", + "name": "q1", + "choicesLazyLoadEnabled": true, + }] + }; + const survey = new SurveyModel(json); + const question = survey.getAllQuestions()[0]; + let log = ""; + survey.onGetChoiceDisplayValue.add((_, opt) => { + setTimeout(() => { + log += "->onGetChoiceDisplayValue"; + opt.setItems(["Ford"]); + }, 1); + }); + question.onReadyChanged.add((_, opt) => { + log += `->onReadyChanged: ${opt.isReady}`; + if(opt.isReady) { + assert.ok(question.isReady); + assert.notOk(question["waitingAcyncOperations"]); + assert.notOk(question["waitingChoicesByURL"]); + assert.notOk(question["waitingGetChoiceDisplayValueResponse"]); + assert.equal(log, "->onReadyChanged: false->onGetChoiceDisplayValue->onReadyChanged: true"); + assert.equal(question.displayValue, "Ford"); + done(); + } + }); + survey.data = { "q1": "ford" }; + assert.notOk(question.isReady); + assert.ok(question["waitingAcyncOperations"]); + assert.notOk(question["waitingChoicesByURL"]); + assert.ok(question["waitingGetChoiceDisplayValueResponse"]); +}); + +QUnit.test("isReady flag + onGetChoiceDisplayValue + choicesRestfull", assert => { + const done = assert.async(); + + const json = { + questions: [{ + "type": "dropdown", + "name": "q1", + "choicesLazyLoadEnabled": true, + }] + }; + const survey = new SurveyModel(json); + const question = survey.getAllQuestions()[0]; + let log = ""; + survey.onGetChoiceDisplayValue.add((_, opt) => { + setTimeout(() => { + log += "->onGetChoiceDisplayValue"; + opt.setItems(["Ford"]); + }, 1); + }); + question.onReadyChanged.add((_, opt) => { + log += `->onReadyChanged: ${opt.isReady}`; + if(opt.isReady) { + assert.ok(question.isReady); + assert.notOk(question["waitingAcyncOperations"]); + assert.notOk(question["waitingChoicesByURL"]); + assert.notOk(question["waitingGetChoiceDisplayValueResponse"]); + assert.equal(log, "->onReadyChanged: false->onGetChoiceDisplayValue->onReadyChanged: true"); + done(); + } + }); + question.choicesByUrl.url = "some url"; + survey.data = { "q1": "ford" }; + assert.notOk(question.isReady); + assert.ok(question["waitingAcyncOperations"]); + assert.ok(question["waitingChoicesByURL"]); + assert.ok(question["waitingGetChoiceDisplayValueResponse"]); + question.choicesLoaded(); + assert.notOk(question.isReady); + assert.ok(question["waitingAcyncOperations"]); + assert.notOk(question["waitingChoicesByURL"]); + assert.ok(question["waitingGetChoiceDisplayValueResponse"]); }); \ No newline at end of file