Skip to content

Commit

Permalink
feat(Field): api spliceArray to splice Array like name.{index}
Browse files Browse the repository at this point in the history
  • Loading branch information
bindoon committed Feb 2, 2019
1 parent f90a747 commit 484841b
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 25 deletions.
19 changes: 6 additions & 13 deletions docs/field/demo/dynamic.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@

- order: 6

Input 的 name 必须全局唯一, 否则可能会出现串行的错误
通过 `spliceArray` 可以删除数组格式 name (eg: name.{index}) 的数据, 并且自动订正其他 name 的 index - 1 问题

:::lang=en-us
# mix usage

- order: 6

name of Input must be unique
by use `spliceArray` could delete array type keys (eg: name.{index}), and make larger keys auto -1
:::
---

Expand All @@ -31,7 +31,6 @@ class Demo extends React.Component {

this.field = new Field(this, {
parseName: true,
autoUnmount: true
});
}

Expand All @@ -52,11 +51,11 @@ class Demo extends React.Component {
removeItem(index) {
const { tableSource } = this.state;
tableSource.splice(index, 1);
this.field.spliceArray('name.{index}', index);
this.setState({ tableSource });
}

// name.${value} 全局唯一
input = (value) => <Input {...this.field.init(`name.${value}`, { initValue: value })} />;
input = (value, index) => <Input {...this.field.init(`name.${index}`, { initValue: index })} />;
delete = (value, index) => <Button warning onClick={this.removeItem.bind(this, index)}>delete</Button>;

render() {
Expand All @@ -68,8 +67,8 @@ class Demo extends React.Component {
<Table.Column title="operation" cell={this.delete} width={100} />
</Table>
<div style={{ marginTop: 10 }}>
<Button type="primary" onClick={this.getValues}>print</Button>
<Button type="normal" onClick={this.add} style={{ marginLeft: 4 }}>Add</Button>
<Button type="primary" onClick={this.getValues} >print</Button>
<Button type="normal" onClick={this.add} style={{ marginLeft: 8 }}>Add</Button>
</div>
</div>
);
Expand All @@ -78,9 +77,3 @@ class Demo extends React.Component {

ReactDOM.render(<Demo />, mountNode);
````

````css
.demo .next-btn {
margin-right: 5px;
}
````
1 change: 1 addition & 0 deletions docs/field/index.en-us.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ The api interface provided by the object after `new` (eg `myfield.getValues()`)
|getState | Judge check status | Function(name: String)| 'error' 'success' 'loading' '' | '' |
| getNames | Get the key of all components | Function()| ||
|remove | Delete the data of a certain control or a group of controls. After deletion, the validate/value associated with it will be cleared. | Function(name: String/String[])|
| spliceArray | delete data of name like name.{index} | Function(keyMatch: String, index: Number)| | |

#### init
```
Expand Down
2 changes: 1 addition & 1 deletion docs/field/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ let myfield = new Field(this [,options]);
| getState | 判断校验状态 | Function(name: String)| 'error' 'success' 'loading' '' | '' |
| getNames | 获取所有组件的key | Function()| | |
| remove | 删除某一个或者一组控件的数据,删除后与之相关的validate/value都会被清空 | Function(name: String/String[])| | |

| spliceArray | 删除 name 是数组格式的数据, 并且自动处理其他 name 的数组错位问题 | Function(keyMatch: String, index: Number)| | |

#### init
```
Expand Down
69 changes: 59 additions & 10 deletions src/field/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import ReactDOM from 'react-dom';
import {log, func} from '../util';
import { log, func } from '../util';
import Validate from '../validate';

import {
Expand Down Expand Up @@ -34,10 +34,10 @@ class Field {
scrollToFirstError: true,
first: false,
onChange: func.noop,
autoUnmount: true
autoUnmount: true,
}, options);

['init', 'getValue', 'getValues', 'setValue', 'setValues', 'getError', 'setError', 'setErrors', 'validate', 'getState', 'reset', 'resetToDefault', 'remove'].forEach((m) => {
['init', 'getValue', 'getValues', 'setValue', 'setValues', 'getError', 'setError', 'setErrors', 'validate', 'getState', 'reset', 'resetToDefault', 'remove', 'spliceArray'].forEach((m) => {
this[m] = this[m].bind(this);
});

Expand Down Expand Up @@ -182,16 +182,19 @@ class Field {
}

/**
* saveRef is async function. it will be called after render
* NOTE: saveRef is async function. it will be called after render
* @param {String} name name of component
* @param {Function} component ref
*/
_saveRef(name, component) {
const key = `${name}_field`;
const autoUnmount = this.options.autoUnmount;

if (!component && autoUnmount) {
// component with same name(eg: type? <A name="n"/>:<B name="n"/>)
// while type change to true B will render before A unmount.
// component with same name (eg: type ? <A name="n"/>:<B name="n"/>)
// while type changed, B will render before A unmount. so we should cached value for B
// step: render -> B mount -> 1. _saveRef(A, null) -> 2. _saveRef(B, ref) -> render
// 1. _saveRef(A, null)
const cache = this.fieldsMeta[name];
this._setCache(name, key, cache);
// after destroy, delete data
Expand All @@ -200,7 +203,7 @@ class Field {
return;
}

// after _saveRef(name, null) and before rerender. (eg: same name but different compoent may be here)
// 2. _saveRef(B, ref) (eg: same name but different compoent may be here)
if (autoUnmount && !this.fieldsMeta[name]) {
this.fieldsMeta[name] = this._getCache(name, key);
}
Expand Down Expand Up @@ -235,7 +238,9 @@ class Field {
let validate = this._getCache(name, trigger);
validate && validate.abort();

validate = new Validate({[name]: rule});
validate = new Validate({
[name]: rule
});
this._setCache(name, trigger, validate);

validate.validate({
Expand Down Expand Up @@ -374,7 +379,7 @@ class Field {
* @param {Function} cb callback after validate
*/
validate(ns, cb) {
const {names, callback} = getParams(ns, cb);
const { names, callback } = getParams(ns, cb);
const fieldNames = names || this.getNames();

const descriptor = {};
Expand Down Expand Up @@ -405,7 +410,7 @@ class Field {
return;
}

const validate = new Validate(descriptor, {first: this.options.first});
const validate = new Validate(descriptor, { first: this.options.first });

validate.validate(values, (errors) => {
let errorsGroup = null;
Expand Down Expand Up @@ -542,6 +547,50 @@ class Field {
});
}

/**
* splice in a Array
* @param {String} keyMatch like name.{index}
* @param {Number} startIndex index
*/
spliceArray(keyMatch, startIndex) {
if (keyMatch.indexOf('{index}') === -1) {
log.warning('{index} not find in key');
return;
}

const reg = keyMatch.replace('{index}', '(\\d+)');
const keyReg = new RegExp(`^${reg}$`);

let list = [];
const names = this.getNames();
names.forEach(n => {
const ret = keyReg.exec(n);
if (ret) {
const index = parseInt(ret[1]);
if (index > startIndex) {
list.push({
index,
name: n,
});
}
}
});

list = list.sort((a, b) => a.index < b.index);

// should be continuous array
if (list.length > 0 && list[0].index === startIndex + 1) {
list.forEach(l => {
const n = keyMatch.replace('{index}', l.index - 1);
this.fieldsMeta[n] = this.fieldsMeta[l.name];
});

delete this.fieldsMeta[list[list.length - 1].name];

this._reRender();
}
}

_resetError(name) {
const field = this._get(name);
delete field.errors; //清空错误
Expand Down
21 changes: 20 additions & 1 deletion test/field/index-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ describe('field', () => {
assert(field.getError('input') === null);
assert(field.getError('input2')[0] === "error 2");

field.setError('input', < span > hello < /span>);
field.setError('input', <span>hello</span>);
assert(React.isValidElement(field.getError('input')[0]) === true);

done();
Expand Down Expand Up @@ -384,6 +384,25 @@ describe('field', () => {
assert(field._get('input') === null);
assert(field._get('input2') === null);

done();
});
it('spliceArray', (done) => {
let field = new Field();
field.init('input.0', {initValue: 0});
field.init('input.1', {initValue: 1});
field.init('input.2', {initValue: 2});

field.spliceArray('input.{index}', 1);

assert(field.getValue('input.0') === 0);
assert(field.getValue('input.1') === 2);
assert(field.getValue('input.2') === undefined);

field.spliceArray('input.{index}', 0);
assert(field.getValue('input.0') === 2);
assert(field.getValue('input.1') === undefined);
assert(field.getValue('input.2') === undefined);

done();
});
})
Expand Down

0 comments on commit 484841b

Please sign in to comment.