-
-
`;
exports[`renders ./antdv-demo/docs/form/demo/normal-login.vue correctly 1`] = `
diff --git a/components/n-form/Form.jsx b/components/n-form/Form.jsx
index e46597e55..2e2c15edc 100755
--- a/components/n-form/Form.jsx
+++ b/components/n-form/Form.jsx
@@ -1,16 +1,11 @@
import PropTypes from '../_util/vue-types';
import classNames from 'classnames';
import { ColProps } from '../grid/Col';
-import Vue from 'vue';
import isRegExp from 'lodash/isRegExp';
import warning from '../_util/warning';
-import createDOMForm from '../vc-form/src/createDOMForm';
-import createFormField from '../vc-form/src/createFormField';
import FormItem from './FormItem';
-import { FIELD_META_PROP, FIELD_DATA_PROP } from './constants';
import { initDefaultProps, getListeners } from '../_util/props-util';
import { ConfigConsumerProps } from '../config-provider';
-import Base from '../base';
export const FormProps = {
layout: PropTypes.oneOf(['horizontal', 'inline', 'vertical']),
@@ -144,7 +139,7 @@ const Form = {
if (message) {
valid = false;
}
- invalidFields = objectAssign({}, invalidFields, field);
+ invalidFields = Object.assign({}, invalidFields, field);
if (typeof callback === 'function' && ++count === this.fields.length) {
callback(valid, invalidFields);
}
diff --git a/components/n-form/FormItem.jsx b/components/n-form/FormItem.jsx
index cec3f6f8a..fd293def6 100644
--- a/components/n-form/FormItem.jsx
+++ b/components/n-form/FormItem.jsx
@@ -1,10 +1,19 @@
import AsyncValidator from 'async-validator';
+import cloneDeep from 'lodash/cloneDeep';
import PropTypes from '../_util/vue-types';
import { ColProps } from '../grid/Col';
-import { initDefaultProps, getComponentFromProp, getOptionProps } from '../_util/props-util';
+import {
+ initDefaultProps,
+ getComponentFromProp,
+ getOptionProps,
+ getEvents,
+ filterEmpty,
+ isValidElement,
+} from '../_util/props-util';
import BaseMixin from '../_util/BaseMixin';
import { ConfigConsumerProps } from '../config-provider';
import FormItem from '../form/FormItem';
+import { cloneElement } from '../_util/vnode';
function noop() {}
@@ -45,8 +54,9 @@ export const FormItemProps = {
hasFeedback: PropTypes.bool,
colon: PropTypes.bool,
labelAlign: PropTypes.oneOf(['left', 'right']),
- name: PropTypes.string,
- rules: PropTypes.array,
+ prop: PropTypes.string,
+ rules: PropTypes.oneOfType([Array, Object]),
+ autoLink: PropTypes.bool,
required: PropTypes.bool,
validateStatus: PropTypes.oneOf(['', 'success', 'warning', 'error', 'validating']),
};
@@ -57,6 +67,7 @@ export default {
mixins: [BaseMixin],
props: initDefaultProps(FormItemProps, {
hasFeedback: false,
+ autoLink: true,
}),
provide() {
return {
@@ -88,6 +99,20 @@ export default {
}
return getPropByPath(model, path, true).v;
},
+ isRequired() {
+ let rules = this.getRules();
+ let isRequired = false;
+ if (rules && rules.length) {
+ rules.every(rule => {
+ if (rule.required) {
+ isRequired = true;
+ return false;
+ }
+ return true;
+ });
+ }
+ return isRequired;
+ },
},
watch: {
validateStatus(val) {
@@ -98,11 +123,7 @@ export default {
if (this.prop) {
const { addField } = this.FormContext;
addField && addField(this);
- let initialValue = this.fieldValue;
- if (Array.isArray(initialValue)) {
- initialValue = [...initialValue];
- }
- this.initialValue = initialValue;
+ this.initialValue = cloneDeep(this.fieldValue);
}
},
beforeDestroy() {
@@ -113,7 +134,7 @@ export default {
validate(trigger, callback = noop) {
this.validateDisabled = false;
const rules = this.getFilteredRule(trigger);
- if ((!rules || rules.length === 0) && this.required === undefined) {
+ if (!rules || rules.length === 0) {
callback();
return true;
}
@@ -138,12 +159,13 @@ export default {
});
},
getRules() {
- let formRules = this.FormContext.rules || {};
+ let formRules = this.FormContext.rules;
const selfRules = this.rules;
- const requiredRule = this.required !== undefined ? { required: !!this.required } : [];
+ const requiredRule =
+ this.required !== undefined ? { required: !!this.required, trigger: 'change' } : [];
const prop = getPropByPath(formRules, this.prop || '');
formRules = formRules ? prop.o[this.prop || ''] || prop.v : [];
- return [...selfRules, ...formRules, ...requiredRule];
+ return [].concat(selfRules || formRules || []).concat(requiredRule);
},
getFilteredRule(trigger) {
const rules = this.getRules();
@@ -196,7 +218,7 @@ export default {
},
},
render() {
- const { $slots } = this;
+ const { $slots, $scopedSlots } = this;
const props = getOptionProps(this);
const label = getComponentFromProp(this, 'label');
const extra = getComponentFromProp(this, 'extra');
@@ -208,8 +230,31 @@ export default {
extra,
validateStatus: this.validateState,
help: this.validateMessage || help,
+ required: this.isRequired || props.required,
},
};
- return
{$slots.default};
+ const children = filterEmpty($scopedSlots.default ? $scopedSlots.default() : $slots.default);
+ let firstChildren = children[0];
+ if (this.prop && this.autoLink && isValidElement(firstChildren)) {
+ const originalEvents = getEvents(firstChildren);
+ firstChildren = cloneElement(firstChildren, {
+ on: {
+ blur: (...args) => {
+ originalEvents.blur && originalEvents.blur(...args);
+ this.onFieldBlur();
+ },
+ change: (...args) => {
+ originalEvents.change && originalEvents.change(...args);
+ this.onFieldChange();
+ },
+ },
+ });
+ }
+ return (
+
+ {firstChildren}
+ {children.slice(1)}
+
+ );
},
};
diff --git a/components/n-form/__tests__/__snapshots__/demo.test.js.snap b/components/n-form/__tests__/__snapshots__/demo.test.js.snap
new file mode 100644
index 000000000..94c017595
--- /dev/null
+++ b/components/n-form/__tests__/__snapshots__/demo.test.js.snap
@@ -0,0 +1,282 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders ./antdv-demo/docs/n-form/demo/basic.md correctly 1`] = `
+
+`;
+
+exports[`renders ./antdv-demo/docs/n-form/demo/custom-validation.md correctly 1`] = `
+
+`;
+
+exports[`renders ./antdv-demo/docs/n-form/demo/dynamic-form-item.md correctly 1`] = `
+
+`;
+
+exports[`renders ./antdv-demo/docs/n-form/demo/horizontal-login.md correctly 1`] = `
+
+`;
+
+exports[`renders ./antdv-demo/docs/n-form/demo/layout.md correctly 1`] = `
+
+`;
+
+exports[`renders ./antdv-demo/docs/n-form/demo/validation.md correctly 1`] = `
+
+`;
diff --git a/tests/__snapshots__/index.test.js.snap b/tests/__snapshots__/index.test.js.snap
index 1627daea6..7e20e436b 100644
--- a/tests/__snapshots__/index.test.js.snap
+++ b/tests/__snapshots__/index.test.js.snap
@@ -27,6 +27,7 @@ Array [
"Divider",
"Dropdown",
"Form",
+ "NewForm",
"Icon",
"Input",
"InputNumber",
diff --git a/types/n-form/form-item.d.ts b/types/n-form/form-item.d.ts
new file mode 100644
index 000000000..0120457ee
--- /dev/null
+++ b/types/n-form/form-item.d.ts
@@ -0,0 +1,69 @@
+// Project: https://github.com/vueComponent/ant-design-vue
+// Definitions by: akki-jat
+// Definitions: https://github.com/vueComponent/ant-design-vue/types
+
+import { AntdComponent } from '../component';
+import { Col } from '../grid/col';
+
+export declare class NFormItem extends AntdComponent {
+ /**
+ * Used with label, whether to display : after label text.
+ * @default true
+ * @type boolean
+ */
+ colon: boolean;
+
+ /**
+ * The extra prompt message. It is similar to help. Usage example: to display error message and prompt message at the same time.
+ * @type any (string | slot)
+ */
+ extra: any;
+
+ /**
+ * Used with validateStatus, this option specifies the validation status icon. Recommended to be used only with Input.
+ * @default false
+ * @type boolean
+ */
+ hasFeedback: boolean;
+
+ /**
+ * The prompt message. If not provided, the prompt message will be generated by the validation rule.
+ * @type any (string | slot)
+ */
+ help: any;
+
+ /**
+ * Label test
+ * @type any (string | slot)
+ */
+ label: any;
+
+ /**
+ * The layout of label. You can set span offset to something like {span: 3, offset: 12} or sm: {span: 3, offset: 12} same as with
+ * @type Col
+ */
+ labelCol: Col;
+
+ /**
+ * Whether provided or not, it will be generated by the validation rule.
+ * @default false
+ * @type boolean
+ */
+ required: boolean;
+
+ /**
+ * The validation status. If not provided, it will be generated by validation rule. options: 'success' 'warning' 'error' 'validating'
+ * @type string
+ */
+ validateStatus: '' | 'success' | 'warning' | 'error' | 'validating';
+
+ /**
+ * The layout for input controls, same as labelCol
+ * @type Col
+ */
+ wrapperCol: Col;
+ labelAlign: 'left' | 'right';
+ prop: string;
+ rules: object | array;
+ autoLink: boolean;
+}
diff --git a/types/n-form/form.d.ts b/types/n-form/form.d.ts
new file mode 100644
index 000000000..517184ecb
--- /dev/null
+++ b/types/n-form/form.d.ts
@@ -0,0 +1,115 @@
+// Project: https://github.com/vueComponent/ant-design-vue
+// Definitions by: akki-jat
+// Definitions: https://github.com/vueComponent/ant-design-vue/types
+
+import { AntdComponent } from '../component';
+import { Col } from '../grid/col';
+import Vue from 'vue';
+import { NFormItem } from './form-item';
+
+declare interface ValidationRule {
+ trigger?: string;
+ /**
+ * validation error message
+ * @type string
+ */
+ message?: string;
+
+ /**
+ * built-in validation type, available options: https://github.com/yiminghe/async-validator#type
+ * @default 'string'
+ * @type string
+ */
+ type?: string;
+
+ /**
+ * indicates whether field is required
+ * @default false
+ * @type boolean
+ */
+ required?: boolean;
+
+ /**
+ * treat required fields that only contain whitespace as errors
+ * @default false
+ * @type boolean
+ */
+ whitespace?: boolean;
+
+ /**
+ * validate the exact length of a field
+ * @type number
+ */
+ len?: number;
+
+ /**
+ * validate the min length of a field
+ * @type number
+ */
+ min?: number;
+
+ /**
+ * validate the max length of a field
+ * @type number
+ */
+ max?: number;
+
+ /**
+ * validate the value from a list of possible values
+ * @type string | string[]
+ */
+ enum?: string | string[];
+
+ /**
+ * validate from a regular expression
+ * @type boolean
+ */
+ pattern?: RegExp;
+
+ /**
+ * transform a value before validation
+ * @type Function
+ */
+ transform?: (value: any) => any;
+
+ /**
+ * custom validate function (Note: callback must be called)
+ * @type Function
+ */
+ validator?: (rule: any, value: any, callback: Function) => any;
+}
+
+export declare class NForm extends AntdComponent {
+ static Item: typeof NFormItem;
+
+ /**
+ * Hide required mark of all form items
+ * @default false
+ * @type boolean
+ */
+ hideRequiredMark: boolean;
+
+ /**
+ * The layout of label. You can set span offset to something like {span: 3, offset: 12} or sm: {span: 3, offset: 12} same as with
+ * @type Col
+ */
+ labelCol: Col;
+
+ /**
+ * Define form layout
+ * @default 'horizontal'
+ * @type string
+ */
+ layout: 'horizontal' | 'inline' | 'vertical';
+
+ /**
+ * The layout for input controls, same as labelCol
+ * @type Col
+ */
+ wrapperCol: Col;
+ colon: boolean;
+ labelAlign: 'left' | 'right';
+ model: object;
+ rules: object;
+ validateOnRuleChange: boolean;
+}