Skip to content

Commit ada7411

Browse files
authored
DropDownEditor: async fieldTemplate should render last value if 2 requests are processed in parallel (T1262587, refix) (DevExpress#29474)
1 parent 650749c commit ada7411

File tree

8 files changed

+463
-294
lines changed

8 files changed

+463
-294
lines changed

apps/react/examples/Examples.tsx

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ import TextBoxExample from './text-box-example';
2323
import ToolbarExample from './toolbar-example';
2424
import ValidationExample from './validation-example';
2525
// import DateBoxExample from './date-box-example';
26-
import SelectBoxCustomExample from './selector-custom-example';
26+
import SelectBoxTemplatesExample from './selectbox-templates-example';
27+
import SelectBoxFieldTemplateExample from './selectbox-fieldtemplate-example';
28+
import SelectBoxParallelTemplateRenderExample from './selectbox-parallel-template-render-example';
2729

2830
const Examples = () => {
2931
return (
@@ -86,8 +88,16 @@ const Examples = () => {
8688
<SelectBoxExample />
8789
</Example>
8890

89-
<Example title="SelectBox example with custom components">
90-
<SelectBoxCustomExample />
91+
<Example title="SelectBox example with templates for item and field">
92+
<SelectBoxTemplatesExample />
93+
</Example>
94+
95+
<Example title="SelectBox example with field template (controlled mode)">
96+
<SelectBoxFieldTemplateExample />
97+
</Example>
98+
99+
<Example title="SelectBox example with field template (parallel render)">
100+
<SelectBoxParallelTemplateRenderExample />
91101
</Example>
92102

93103
{/*<StandaloneValidatorExample />*/}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import * as React from 'react';
2+
import { SelectBox } from 'devextreme-react/select-box';
3+
import { TextBox } from 'devextreme-react/text-box';
4+
5+
const nameLabel = { 'aria-label': 'Name' };
6+
7+
function Field(data: { ImageSrc: any; Name: any }) {
8+
return (
9+
<div className="custom-item">
10+
<img
11+
alt="Product name"
12+
src={data && data.ImageSrc}
13+
/>
14+
<TextBox
15+
className="product-name"
16+
inputAttr={nameLabel}
17+
defaultValue={data && data.Name}
18+
readOnly={true}
19+
/>
20+
</div>
21+
);
22+
}
23+
24+
const products = [
25+
{
26+
ID: 1,
27+
Name: "HD Video Player",
28+
Price: 330,
29+
Current_Inventory: 225,
30+
Backorder: 0,
31+
Manufacturing: 10,
32+
Category: "Video Players",
33+
ImageSrc:
34+
"https://js.devexpress.com/React/Demos/WidgetsGallery/JSDemos/images/products/1-small.png",
35+
},
36+
{
37+
ID: 2,
38+
Name: "SuperHD Player",
39+
Price: 400,
40+
Current_Inventory: 150,
41+
Backorder: 0,
42+
Manufacturing: 25,
43+
Category: "Video Players",
44+
ImageSrc:
45+
"https://js.devexpress.com/React/Demos/WidgetsGallery/JSDemos/images/products/2-small.png",
46+
},
47+
];
48+
49+
50+
export default (): React.ReactElement | null => {
51+
const [value, setValue] = React.useState(1);
52+
53+
// NOTE: In real we do some mapping, like `products.map(x => ({ ... }))
54+
const items = [...products];
55+
56+
return (
57+
<SelectBox
58+
showClearButton={true}
59+
dataSource={items}
60+
displayExpr="Name"
61+
valueExpr="ID"
62+
value={value}
63+
onValueChange={setValue}
64+
fieldRender={Field}
65+
/>
66+
);
67+
};
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import * as React from 'react';
2+
import { SelectBox } from 'devextreme-react/select-box';
3+
import { TextBox } from 'devextreme-react/text-box';
4+
5+
const Field = (props) => {
6+
const label = props.data?.text ?? "";
7+
debugger
8+
return <TextBox value={label} placeholder="Search..." />;
9+
};
10+
11+
const items = [
12+
{
13+
value: 0,
14+
text: "Item 0",
15+
},
16+
];
17+
18+
const items2 = [
19+
{
20+
value: 0,
21+
text: "Item 0",
22+
},
23+
];
24+
25+
export default (): React.ReactElement | null => {
26+
27+
const [value, setValue] = React.useState<number | null>(0);
28+
29+
const [switched, setSwitched] = React.useState(false);
30+
const onSwitch = () => {
31+
setValue(null);
32+
setSwitched((prev) => !prev);
33+
};
34+
const onReset = () => setValue(null);
35+
36+
return (
37+
<div>
38+
<button type="button" onClick={onSwitch}>
39+
Reset and switch DS
40+
</button>
41+
<button type="button" onClick={onReset}>
42+
Reset Value
43+
</button>
44+
<SelectBox
45+
dataSource={switched ? items2 : items}
46+
displayExpr="text"
47+
valueExpr="value"
48+
fieldComponent={Field}
49+
value={value}
50+
showClearButton={true}
51+
onValueChange={setValue}
52+
/>
53+
</div>
54+
);
55+
};
File renamed without changes.

apps/react/index.tsx

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,20 @@ themes.initialized(() => {
1515
</React.StrictMode>
1616
);
1717
});
18+
19+
// for React 17
20+
// import 'devextreme/dist/css/dx.common.css';
21+
// import 'devextreme/dist/css/dx.light.css';
22+
23+
// import React from 'react';
24+
// import ReactDOM from 'react-dom';
25+
// import themes from 'devextreme/ui/themes';
26+
27+
// import App from './App';
28+
29+
// themes.initialized(() => {
30+
// ReactDOM.render(
31+
// <App />,
32+
// document.getElementById('app')
33+
// );
34+
// });

packages/devextreme/js/__internal/ui/drop_down_editor/m_drop_down_editor.ts

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ const DROP_DOWN_EDITOR_FIELD_TEMPLATE_WRAPPER = 'dx-dropdowneditor-field-templat
4242
const OVERLAY_CONTENT_LABEL = 'Dropdown';
4343

4444
const isIOs = devices.current().platform === 'ios';
45+
46+
function createTemplateWrapperElement() {
47+
return $('<div>').addClass(DROP_DOWN_EDITOR_FIELD_TEMPLATE_WRAPPER);
48+
}
49+
4550
// @ts-expect-error
4651
const DropDownEditor = TextBox.inherit({
4752

@@ -329,8 +334,7 @@ const DropDownEditor = TextBox.inherit({
329334
}
330335

331336
if (!this._$templateWrapper) {
332-
this._$templateWrapper = $('<div>')
333-
.addClass(DROP_DOWN_EDITOR_FIELD_TEMPLATE_WRAPPER)
337+
this._$templateWrapper = createTemplateWrapperElement()
334338
.prependTo(this.$element());
335339
}
336340
},
@@ -342,20 +346,19 @@ const DropDownEditor = TextBox.inherit({
342346
this._detachFocusEvents();
343347

344348
this._$textEditorContainer.remove();
345-
this._$templateWrapper.empty();
346349

347-
const $templateWrapper = this._$templateWrapper;
350+
const $newTemplateWrapper = createTemplateWrapperElement();
351+
this._$templateWrapper!.replaceWith($newTemplateWrapper);
352+
this._$templateWrapper = $newTemplateWrapper;
348353

349354
const currentRenderContext = Symbol('renderContext');
350355
this._activeRenderContext = currentRenderContext;
351356

352357
fieldTemplate.render({
353358
model: data,
354-
container: getPublicElement($templateWrapper),
359+
container: getPublicElement(this._$templateWrapper),
355360
onRendered: () => {
356361
if (this._activeRenderContext !== currentRenderContext) {
357-
$templateWrapper.empty();
358-
359362
return;
360363
}
361364

0 commit comments

Comments
 (0)