scripts/django_cradmin/widgets/AbstractSelectWidget.jsx
import React from "react";
import ReactDOM from "react-dom";
import AbstractWidget from "ievv_jsbase/widget/AbstractWidget";
import HttpDjangoJsonRequest from 'ievv_jsbase/http/HttpDjangoJsonRequest';
export default class AbstractSelectWidget extends AbstractWidget {
constructor(element, widgetInstanceId) {
super(element, widgetInstanceId);
this.logger = new window.ievv_jsbase_core.LoggerSingleton().getLogger(
'django_cradmin.widgets.AbstractSelectWidget');
this.onSelectResultSignal = this.onSelectResultSignal.bind(this);
this.onSearchRequestedSignal = this.onSearchRequestedSignal.bind(this);
this._uniquePrefix = `django_cradmin.Select.${this.widgetInstanceId}`;
this._searchRequestedSignalName = `${this._uniquePrefix}.SearchRequested`;
this._searchCompletedSignalName = `${this._uniquePrefix}.SearchCompleted`;
this._selectResultSignalName = `${this._uniquePrefix}.SelectResult`;
this.initialValue = this._getInitialValue();
this.selectedValue = this.initialValue;
this.logger.debug(`initialValue: "${this.initialValue}"`);
this._initializeSignalHandlers();
if(this.initialValue != null) {
this._loadPreviewForInitialValue();
} else {
this._updateUiForEmptyValue();
}
}
getDefaultConfig() {
return {
valueAttribute: 'id',
fetchEmptySearchOnLoad: false,
toggleElementsOnValueChange: {
loading: [],
hasValue: [],
noValue: []
},
updateInnerHtmlWithResult: {},
clientsideSearch: {},
searchApi: {
url: null,
staticData: {}
},
componentProps: {
search: {},
resultList: {},
result: {}
}
}
}
_getInitialValue() {
let initialValue = null;
if(this.config.valueTargetInputId) {
initialValue = document.getElementById(this.config.valueTargetInputId).value;
} else if (this.config.initialValue) {
initialValue = this.config.initialValue;
}
if(initialValue == undefined || initialValue == '') {
initialValue = null;
}
return initialValue;
}
_initializeSignalHandlers() {
new window.ievv_jsbase_core.SignalHandlerSingleton().addReceiver(
this._searchRequestedSignalName,
'django_cradmin.widgets.AbstractSelectWidget',
this.onSearchRequestedSignal
);
new window.ievv_jsbase_core.SignalHandlerSingleton().addReceiver(
this._selectResultSignalName,
'django_cradmin.widgets.AbstractSelectWidget',
this.onSelectResultSignal
);
}
destroy() {
this.element.removeEventListener('click', this._onClick);
new window.ievv_jsbase_core.SignalHandlerSingleton().removeReceiver(
this._searchRequestedSignalName,
'django_cradmin.widgets.AbstractSelectWidget'
);
new window.ievv_jsbase_core.SignalHandlerSingleton().removeReceiver(
this._selectResultSignalName,
'django_cradmin.widgets.AbstractSelectWidget'
);
if(this._reactWrapperElement) {
ReactDOM.unmountComponentAtNode(this._reactWrapperElement);
this._reactWrapperElement.remove();
}
}
_setValueTargetValue(value) {
if(this.config.valueTargetInputId) {
document.getElementById(this.config.valueTargetInputId).value = value;
}
}
_hideElementById(domId) {
const element = document.getElementById(domId);
if(element) {
element.setAttribute('style', 'display: none');
}
}
_showElementById(domId) {
const element = document.getElementById(domId);
if(element) {
element.setAttribute('style', 'display: block');
}
}
_hideElementsById(domIdArray) {
for(let domId of domIdArray) {
this._hideElementById(domId);
}
}
_showElementsById(domIdArray) {
for(let domId of domIdArray) {
this._showElementById(domId);
}
}
_updatePreviews(resultObject) {
for(let attribute of Object.keys(this.config.updateInnerHtmlWithResult)) {
let domIds = this.config.updateInnerHtmlWithResult[attribute];
let value = resultObject[attribute];
if(value != undefined && value != null) {
for(let domId of domIds) {
const element = document.getElementById(domId);
if(element) {
element.innerHTML = value;
}
}
}
}
}
_getValueFromResultObject(resultObject) {
return resultObject[this.config.valueAttribute];
}
_setLoading() {
this._hideElementsById(this.config.toggleElementsOnValueChange.hasValue);
this._hideElementsById(this.config.toggleElementsOnValueChange.noValue);
this._showElementsById(this.config.toggleElementsOnValueChange.loading);
}
_updateUiForEmptyValue() {
this.selectedValue = null;
this._hideElementsById(this.config.toggleElementsOnValueChange.hasValue);
this._hideElementsById(this.config.toggleElementsOnValueChange.loading);
this._showElementsById(this.config.toggleElementsOnValueChange.noValue);
this._setValueTargetValue('');
}
_updateUiFromResultObject(resultObject) {
let value = this._getValueFromResultObject(resultObject);
this.selectedValue = value;
this._hideElementsById(this.config.toggleElementsOnValueChange.noValue);
this._hideElementsById(this.config.toggleElementsOnValueChange.loading);
this._showElementsById(this.config.toggleElementsOnValueChange.hasValue);
this._setValueTargetValue(value);
this._updatePreviews(resultObject);
}
onSelectResultSignal(receivedSignalInfo) {
this.logger.debug(receivedSignalInfo.toString());
let resultObject = receivedSignalInfo.data;
if(resultObject == null) {
this._updateUiForEmptyValue();
} else {
this._updateUiFromResultObject(resultObject);
}
}
_useServerSideSearch() {
return this.config.searchApi.url != undefined && this.config.searchApi.url != null;
}
onSearchRequestedSignal(receivedSignalInfo) {
this.logger.debug(receivedSignalInfo.toString());
const searchString = receivedSignalInfo.data;
this.requestSearchResults(searchString)
.then((results) => {
this.sendSearchCompletedSignal(results);
})
.catch((error) => {
throw error;
});
}
sendSearchCompletedSignal(results) {
this.logger.debug('Search complete. Result:', results);
new window.ievv_jsbase_core.SignalHandlerSingleton().send(
this._searchCompletedSignalName,
results
);
}
_isClientSideSearchMatch(searchString, resultObject) {
for(let attribute of this.config.clientsideSearch.searchAttributes) {
if(resultObject[attribute] != undefined && resultObject[attribute] != null) {
if(resultObject[attribute].toLowerCase().indexOf(searchString) != -1) {
return true;
}
}
}
return false;
}
_requestClientSideSearchResults(searchString) {
return new Promise((resolve, reject) => {
const resultObjectArray = [];
searchString = searchString.toLowerCase();
for (let resultObject of this.config.clientsideSearch.data) {
if (this._isClientSideSearchMatch(searchString, resultObject)) {
resultObjectArray.push(resultObject);
}
}
resolve({
count: resultObjectArray.size,
results: resultObjectArray
});
});
}
_requestServerSideSearchResults(searchString) {
return new Promise((resolve, reject) => {
const request = new HttpDjangoJsonRequest(this.config.searchApi.url);
for (let attribute of Object.keys(this.config.searchApi.staticData)) {
request.urlParser.queryString.set(
attribute, this.config.searchApi.staticData[attribute]);
}
request.urlParser.queryString.set('search', searchString);
request.get()
.then((response) => {
resolve(response.bodydata);
})
.catch((error) => {
reject(error);
});
});
}
requestSearchResults(searchString='') {
if(this._useServerSideSearch()) {
return this._requestServerSideSearchResults(searchString);
} else {
return this._requestClientSideSearchResults(searchString);
}
}
_requestSingleResultServerSide(value) {
return new Promise((resolve, reject) => {
let url = this.config.searchApi.url;
if(!this.config.searchApi.url.endsWith('/')) {
url = `${url}/`;
}
url = `${url}${value}`;
const request = new HttpDjangoJsonRequest(url);
request.get()
.then((response) => {
resolve(response.bodydata);
})
.catch((error) => {
reject(error);
});
});
}
_requestSingleResultClientSide(value) {
return new Promise((resolve, reject) => {
for(let resultObject of this.config.clientsideSearch.data) {
if(this._getValueFromResultObject(resultObject) == value) {
resolve(resultObject);
}
}
reject(new Error(
`config.clientsideSearch.data does not contain an ` +
`object with ${this.config.valueAttribute} = "${value}"`
));
});
}
_requestSingleResult(value) {
if(this._useServerSideSearch()) {
return this._requestSingleResultServerSide(value);
} else {
return this._requestSingleResultClientSide(value);
}
}
_loadPreviewForInitialValue() {
this._setLoading();
this._requestSingleResult(this.initialValue)
.then((resultObject) => {
this.logger.debug(`Loaded data for initialValue ("${this.initialValue}"):`, resultObject);
this._updateUiFromResultObject(resultObject);
})
.catch((error) => {
this.logger.error(
`Failed to load data for initialValue: "${this.initialValue}". ` +
`Error details: ${error.toString()}`);
});
}
addReactWrapperElementToDocument(reactWrapperElement) {
this.element.parentNode.insertBefore(reactWrapperElement, this.element.nextSibling);
}
initializeReactComponent() {
this._reactWrapperElement = document.createElement('div');
this.addReactWrapperElementToDocument(this._reactWrapperElement);
const reactElement = this.makeReactElement();
ReactDOM.render(
reactElement,
this._reactWrapperElement
);
if(this.config.fetchEmptySearchOnLoad) {
this.requestSearchResults()
.then((results) => {
this.sendSearchCompletedSignal(results);
})
.catch((error) => {
throw error;
});
}
}
makeReactComponentProps() {
return {
closeCallback: this._onClose,
selectResultSignalName: this._selectResultSignalName,
searchCompletedSignalName: this._searchCompletedSignalName,
searchRequestedSignalName: this._searchRequestedSignalName,
valueAttribute: this.config.valueAttribute,
searchComponentProps: this.config.componentProps.search,
resultListComponentProps: this.config.componentProps.resultList,
resultComponentProps: this.config.componentProps.result,
selectedValue: this.selectedValue
}
}
makeReactElement() {
throw new Error('You must override makeReactElement()');
}
}