Skip to content

Commit

Permalink
Fix for updating selected value when data is changing (#55)
Browse files Browse the repository at this point in the history
  • Loading branch information
rkit committed Nov 24, 2016
1 parent c86366c commit 8f63613
Show file tree
Hide file tree
Showing 2 changed files with 125 additions and 70 deletions.
73 changes: 62 additions & 11 deletions examples/src/components/Tags.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,23 @@ export default class Tags extends Component {
value1: ['feature'],
value2: ['feature'],
value3: null,
value10: null,
onChange10: () => {
console.log('onChange');
},
data7: [
{ text: 'bug', id: 1 },
{ text: 'feature', id: 2 },
{ text: 'documents', id: 3 },
{ text: 'discussion', id: 4 },
],
data9: ['bug', 'feature', 'documents', 'discussion'],
data10: [
{ text: 'bug', id: 1 },
{ text: 'feature', id: 2 },
{ text: 'documents', id: 3 },
{ text: 'discussion', id: 4 },
],
placeholder9: 'search by tags',
};
}
Expand All @@ -24,7 +34,7 @@ export default class Tags extends Component {
const { value1 } = this.state;
return (
<div>
Update `value`<br/>
Set `bug` `discussion`<br/>
<Select2
multiple
data={['bug', 'feature', 'documents', 'discussion']}
Expand All @@ -37,7 +47,7 @@ export default class Tags extends Component {
/>
<button onClick={() => this.setState({ value1: ['bug', 'discussion'] })}>
set `bug` `discussion` value
Update
</button>
</div>
);
Expand All @@ -47,7 +57,7 @@ export default class Tags extends Component {
const { value2 } = this.state;
return (
<div>
Update mutated `value`<br/>
Set mutated `documents`<br/>
<Select2
multiple
data={['bug', 'feature', 'documents', 'discussion']}
Expand All @@ -66,7 +76,7 @@ export default class Tags extends Component {
this.setState({ value2: items });
}}
>
add `documents` value
Update
</button>
</div>
);
Expand All @@ -76,7 +86,7 @@ export default class Tags extends Component {
const { value3 } = this.state;
return (
<div>
Data as an object<br/>
Set `documents`<br/>
<Select2
multiple={false}
data={[
Expand All @@ -93,7 +103,7 @@ export default class Tags extends Component {
/>
<button onClick={() => this.setState({ value3: 3 })}>
set `documents` value
Update
</button>
</div>
);
Expand Down Expand Up @@ -166,7 +176,7 @@ export default class Tags extends Component {
const { data7 } = this.state;
return (
<div>
Dynamic update data<br/>
Set a new data<br/>
<Select2
defaultValue={1}
data={ data7 }
Expand All @@ -184,7 +194,7 @@ export default class Tags extends Component {
],
})}
>
reload data value
Update
</button>
</div>
);
Expand Down Expand Up @@ -219,7 +229,7 @@ export default class Tags extends Component {
const { placeholder9, data9 } = this.state;
return (
<div>
Update Options<br/>
Set placeholder and a new data<br/>
<Select2
multiple
data={data9}
Expand All @@ -230,8 +240,48 @@ export default class Tags extends Component {
}
/>
<button onClick={() => this.setState({ placeholder9: 'test', data9: ['feature'] })}>
set `placeholder` and `data`
<button onClick={() => this.setState({
placeholder9: 'test',
data9: ['feature'],
})}
>
Update
</button>
</div>
);
}

example10() {
const { value10, data10, onChange10 } = this.state;
return (
<div>
Add a new item and set a new `onChange` event<br/>
<Select2
multiple={false}
data={data10}
defaultValue={ 1 }
value={ value10 }
onChange={onChange10}
options={{
placeholder: 'search by tags',
}}
/>
<button onClick={() => this.setState({
value10: 5,
data10: [
{ text: 'bug', id: 1 },
{ text: 'feature', id: 2 },
{ text: 'documents', id: 3 },
{ text: 'discussion', id: 4 },
{ text: 'tester', id: 5 },
],
onChange10: () => {
console.log('onChange!');
},
})}
>
Update
</button>
</div>
);
Expand All @@ -249,6 +299,7 @@ export default class Tags extends Component {
{this.example7()}<br/>
{this.example8()}<br/>
{this.example9()}<br/>
{this.example10()}<br/>
</div>
);
}
Expand Down
122 changes: 63 additions & 59 deletions src/components/Select2.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,109 +45,113 @@ export default class Select2 extends Component {
constructor(props) {
super(props);
this.el = null;
this.forceUpdateValue = false;
}

componentDidMount() {
this.initSelect2();
this.initSelect2(this.props);
this.updateValue();
}

componentWillReceiveProps(nextProps) {
if (this.el) {
this.setValue(this.prepareValue(nextProps.value, this.props.defaultValue));
}
this.updSelect2(nextProps);
}

componentDidUpdate(prevProps) {
if (!shallowEqualFuzzy(prevProps.data, this.props.data)) {
this.destroySelect2(false);
this.initSelect2(false);
} else {
const { options } = this.props;
if (!shallowEqualFuzzy(prevProps.options, options)) {
this.el.select2(this.prepareOptions(options));
}
}

const handlerChanged = e => prevProps[e[1]] !== this.props[e[1]];

if (this.props.events.some(handlerChanged)) {
this.detachEventHandlers();
this.attachEventHandlers();
}
componentDidUpdate() {
this.updateValue();
}

componentWillUnmount() {
this.destroySelect2();
}

setValue(value) {
const elVal = this.props.multiple ? this.el.val() || [] : this.el.val();
if (!shallowEqualFuzzy(elVal, value)) {
this.el.val(value).trigger('change');
initSelect2(props, updateValue = false) {
const { options } = props;

this.el = $(ReactDOM.findDOMNode(this));
// fix for updating selected value when data is changing
if (updateValue) {
this.forceUpdateValue = true;
this.el.off(`change.${namespace}`).val(null).trigger('change');
}
this.el.select2(this.prepareOptions(options));
this.attachEventHandlers(props);
}

prepareValue(value, defaultValue) {
const issetValue = typeof value !== 'undefined' && value !== null;
const issetDefaultValue = typeof defaultValue !== 'undefined';
updSelect2(props) {
const prevProps = this.props;

if (!issetValue && issetDefaultValue) {
return defaultValue;
if (!shallowEqualFuzzy(prevProps.data, props.data)) {
this.destroySelect2(false);
this.initSelect2(props, true);
} else {
const { options } = props;
if (!shallowEqualFuzzy(prevProps.options, options)) {
this.el.select2(this.prepareOptions(options));
}
}
return value;
}

prepareOptions(options) {
const opt = options;
if (typeof opt.dropdownParent === 'string') {
opt.dropdownParent = $(opt.dropdownParent);
const handlerChanged = e => prevProps[e[1]] !== props[e[1]];
if (props.events.some(handlerChanged)) {
this.detachEventHandlers(props);
this.attachEventHandlers(props);
}
return opt;
}

initSelect2(withCallbacks = true) {
if (this.el) { return; }
const { defaultValue, value, options } = this.props;

this.el = $(ReactDOM.findDOMNode(this));
this.el.select2(this.prepareOptions(options));

if (withCallbacks) {
this.attachEventHandlers();
}
updateValue() {
const { value, defaultValue, multiple } = this.props;
const newValue = this.prepareValue(value, defaultValue);
const currentValue = multiple ? this.el.val() || [] : this.el.val();

if (typeof defaultValue === 'undefined' && typeof value !== 'undefined') {
this.setValue(value);
if (!shallowEqualFuzzy(currentValue, newValue) || this.forceUpdateValue) {
this.el.val(newValue).trigger('change');
this.forceUpdateValue = false;
}
}

destroySelect2(withCallbacks = true) {
if (!this.el) { return; }

if (withCallbacks) {
this.detachEventHandlers();
this.detachEventHandlers(this.props);
}

this.el.select2('destroy');
this.el = null;
}

attachEventHandlers() {
this.props.events.forEach(event => {
if (typeof this.props[event[1]] !== 'undefined') {
this.el.on(event[0], this.props[event[1]]);
attachEventHandlers(props) {
props.events.forEach(event => {
if (typeof props[event[1]] !== 'undefined') {
this.el.on(event[0], props[event[1]]);
}
});
}

detachEventHandlers() {
this.props.events.forEach(event => {
if (typeof this.props[event[1]] !== 'undefined') {
detachEventHandlers(props) {
props.events.forEach(event => {
if (typeof props[event[1]] !== 'undefined') {
this.el.off(event[0]);
}
});
}

prepareValue(value, defaultValue) {
const issetValue = typeof value !== 'undefined' && value !== null;
const issetDefaultValue = typeof defaultValue !== 'undefined';

if (!issetValue && issetDefaultValue) {
return defaultValue;
}
return value;
}

prepareOptions(options) {
const opt = options;
if (typeof opt.dropdownParent === 'string') {
opt.dropdownParent = $(opt.dropdownParent);
}
return opt;
}

isObject(value) {
const type = typeof value;
return type === 'function' || (value && type === 'object') || false;
Expand Down

0 comments on commit 8f63613

Please sign in to comment.