diff --git a/src/core/core.js b/src/core/core.js index b1288e975..b18aa319f 100644 --- a/src/core/core.js +++ b/src/core/core.js @@ -321,11 +321,13 @@ export default class Core { } this.updateHistoryAfterSearch(queryTrigger); window.performance.mark('yext.answers.verticalQueryResponseRendered'); - }).catch(error => { - console.error('Vertical search failed with the following error: ' + error); - this.storage.set(StorageKeys.VERTICAL_RESULTS, new VerticalResults()); - this.storage.set(StorageKeys.DIRECT_ANSWER, new DirectAnswer()); - this.storage.set(StorageKeys.LOCATION_BIAS, new LocationBias({})); + }) + .catch(error => { + this._markSearchComplete(Searcher.VERTICAL); + throw error; + }) + .catch(error => { + console.error('The following problem was encountered during vertical search: ' + error); }); } @@ -415,14 +417,45 @@ export default class Core { } this.updateHistoryAfterSearch(queryTrigger); window.performance.mark('yext.answers.universalQueryResponseRendered'); - }).catch(error => { - console.error('Universal search failed with the following error: ' + error); - this.storage.set(StorageKeys.UNIVERSAL_RESULTS, new UniversalResults({})); - this.storage.set(StorageKeys.DIRECT_ANSWER, new DirectAnswer()); - this.storage.set(StorageKeys.LOCATION_BIAS, new LocationBias({})); + }) + .catch(error => { + this._markSearchComplete(Searcher.UNIVERSAL); + throw error; + }) + .catch(error => { + console.error('The following problem was encountered during universal search: ' + error); }); } + /** + * Update the search state of the results in storage to SEARCH_COMPLETE + * when handling errors from universal and vertical search. This will + * trigger a rerender of the components, which could potentially throw + * a new error. + * + * @param {Searcher} searcherType + */ + _markSearchComplete (searcherType) { + const resultStorageKey = searcherType === Searcher.UNIVERSAL + ? StorageKeys.UNIVERSAL_RESULTS + : StorageKeys.VERTICAL_RESULTS; + const results = this.storage.get(resultStorageKey); + if (results && results.searchState !== SearchStates.SEARCH_COMPLETE) { + results.searchState = SearchStates.SEARCH_COMPLETE; + this.storage.set(resultStorageKey, results); + } + const directanswer = this.storage.get(StorageKeys.DIRECT_ANSWER); + if (directanswer && directanswer.searchState !== SearchStates.SEARCH_COMPLETE) { + directanswer.searchState = SearchStates.SEARCH_COMPLETE; + this.storage.set(StorageKeys.DIRECT_ANSWER, directanswer); + } + const locationbias = this.storage.get(StorageKeys.LOCATION_BIAS); + if (locationbias && locationbias.searchState !== SearchStates.SEARCH_COMPLETE) { + locationbias.searchState = SearchStates.SEARCH_COMPLETE; + this.storage.set(StorageKeys.LOCATION_BIAS, locationbias); + } + } + /** * Builds the object passed as a parameter to onUniversalSearch. This object * contains information about the universal search's query and result counts. diff --git a/src/core/models/directanswer.js b/src/core/models/directanswer.js index 4717ba3f8..2be7219f7 100644 --- a/src/core/models/directanswer.js +++ b/src/core/models/directanswer.js @@ -5,7 +5,6 @@ import SearchStates from '../storage/searchstates'; export default class DirectAnswer { constructor (directAnswer = {}) { Object.assign(this, { searchState: SearchStates.SEARCH_COMPLETE }, directAnswer); - Object.freeze(this); } /** diff --git a/src/core/models/verticalresults.js b/src/core/models/verticalresults.js index e20b1aa1f..5623181b3 100644 --- a/src/core/models/verticalresults.js +++ b/src/core/models/verticalresults.js @@ -17,8 +17,6 @@ export default class VerticalResults { * @type {ResultsContext} */ this.resultsContext = data.resultsContext; - - Object.freeze(this); } /** diff --git a/src/ui/components/component.js b/src/ui/components/component.js index baaa715c5..f8ba7d539 100644 --- a/src/ui/components/component.js +++ b/src/ui/components/component.js @@ -376,20 +376,38 @@ export default class Component { // Process the DOM to determine if we should create // in-memory sub-components for rendering const domComponents = DOM.queryAll(this._container, '[data-component]:not([data-is-component-mounted])'); - const data = this.transformData - ? this.transformData(cloneDeep(this._state.get())) - : this._state.get(); - domComponents.forEach(c => this._createSubcomponent(c, data)); - + let data; + try { + data = this.transformData + ? this.transformData(cloneDeep(this._state.get())) + : this._state.get(); + } catch (e) { + console.error(`The following problem occurred while transforming data for sub-components of ${this.name}: `, e); + } + domComponents.forEach(c => { + try { + this._createSubcomponent(c, data); + } catch (e) { + console.error('The following problem occurred while initializing sub-component: ', c, e); + } + }); if (this._progressivelyRenderChildren) { this._children.forEach(child => { setTimeout(() => { - child.mount(); + try { + child.mount(); + } catch (e) { + console.error('The following problem occurred while mounting sub-component: ', child, e); + } }); }); } else { this._children.forEach(child => { - child.mount(); + try { + child.mount(); + } catch (e) { + console.error('The following problem occurred while mounting sub-component: ', child, e); + } }); }