refactor(form-item): refactor event mechanism

This commit is contained in:
07akioni 2019-11-02 23:09:36 +08:00
parent fdad44af2b
commit 2621d152f9
7 changed files with 113 additions and 43 deletions

View File

@ -6,13 +6,13 @@
:value="formValue" :value="formValue"
ref="form" ref="form"
> >
<n-form-item label="Name" path="name"> <n-form-item label="Name" path="user.name" required>
<n-input v-model="formValue.name" placeholder="Input Name" /> <n-input v-model="formValue.user.name" placeholder="Input Name" />
</n-form-item> </n-form-item>
<n-form-item label="Age" path="age"> <n-form-item label="Age" path="user.age" required>
<n-input placeholder="Input Age" v-model="formValue.age"/> <n-input placeholder="Input Age" v-model="formValue.user.age"/>
</n-form-item> </n-form-item>
<n-form-item label="Phone" path="phone"> <n-form-item label="Phone" path="phone" required>
<n-input placeholder="Phone Number" v-model="formValue.phone"/> <n-input placeholder="Phone Number" v-model="formValue.phone"/>
</n-form-item> </n-form-item>
<n-form-item v-model="formValue.phone"> <n-form-item v-model="formValue.phone">
@ -29,8 +29,10 @@ export default {
data () { data () {
return { return {
formValue: { formValue: {
name: '', user: {
age: '', name: '',
age: ''
},
phone: '' phone: ''
} }
} }

View File

@ -26,7 +26,12 @@
> >
<slot /> <slot />
<div :class="`n-form-item-explain`"> <div :class="`n-form-item-explain`">
{{ explain }} <transition name="n-fade-down">
<span
v-if="explain"
class="n-form-item-explain__content"
>{{ explain }}</span>
</transition>
</div> </div>
</div> </div>
</div> </div>
@ -131,10 +136,17 @@ export default {
}, },
synthesizedRules () { synthesizedRules () {
let rules = [] let rules = []
const validator = (rule, value) => {
if (value !== undefined && value !== null && value !== '') {
return true
}
return Error(`${this.label || this.path} is required!`)
}
if (this.required) { if (this.required) {
rules.push({ rules.push({
trigger: 'blur', trigger: ['blur', 'change', 'input'],
required: true required: true,
validator
}) })
} }
if (this.rule) { if (this.rule) {
@ -156,6 +168,11 @@ export default {
} }
}, },
created () { created () {
/**
* This is buggy!
* Because if it's child change, rules is not updated
* However I need to make it work first
*/
this.addValidationEventListeners() this.addValidationEventListeners()
}, },
methods: { methods: {
@ -165,6 +182,12 @@ export default {
handleContentChange () { handleContentChange () {
this.validate('change') this.validate('change')
}, },
handleContentFocus () {
this.validate('focus')
},
handleContentInput () {
this.validate('input')
},
validate (trigger = null, callback = null) { validate (trigger = null, callback = null) {
if (!this.path) { if (!this.path) {
return return
@ -172,10 +195,17 @@ export default {
const rules = this.synthesizedRules const rules = this.synthesizedRules
const path = this.path const path = this.path
const value = get(this.NForm.value, this.path, null) const value = get(this.NForm.value, this.path, null)
const activeRules = !trigger ? rules : rules.filter(rule => rule.trigger === trigger) const activeRules = !trigger ? rules : rules.filter(rule => {
if (Array.isArray(rule.trigger)) {
return rule.trigger.includes(trigger)
} else {
return rule.trigger === trigger
}
})
if (!activeRules.length) return if (!activeRules.length) return
const asyncValidator = new AsyncValidator({ [path]: activeRules }) const asyncValidator = new AsyncValidator({ [path]: activeRules })
asyncValidator.validate({ [path]: value }, (errors, fields) => { asyncValidator.validate({ [path]: value }, (errors, fields) => {
console.log('validate', errors, fields)
if (errors) { if (errors) {
this.explain = errors[0].message this.explain = errors[0].message
this.validated = true this.validated = true
@ -192,8 +222,10 @@ export default {
addValidationEventListeners () { addValidationEventListeners () {
const rules = this.synthesizedRules const rules = this.synthesizedRules
if (rules.length >= 0) { if (rules.length >= 0) {
this.$on('form-item-blur', this.handleContentBlur) this.$on('blur', this.handleContentBlur)
this.$on('form-item-change', this.handleContentChange) this.$on('input', this.handleContentInput)
this.$on('focus', this.handleContentFocus)
this.$on('change', this.handleContentChange)
} }
} }
} }

View File

@ -29,7 +29,7 @@ export default {
}, },
labelPosition: { labelPosition: {
type: String, type: String,
default: 'top' // ['top', 'right', 'left', 'center'] default: 'top'
}, },
value: { value: {
type: Object, type: Object,

View File

@ -124,22 +124,17 @@
</template> </template>
<script> <script>
import Emitter from '../../../mixins/emitter'
import NCancelMark from '../../../base/CancelMark' import NCancelMark from '../../../base/CancelMark'
import withapp from '../../../mixins/withapp' import withapp from '../../../mixins/withapp'
import themeable from '../../../mixins/themeable' import themeable from '../../../mixins/themeable'
import asformitem from '../../../mixins/asformitem'
export default { export default {
name: 'NInput', name: 'NInput',
components: { components: {
NCancelMark NCancelMark
}, },
mixins: [ withapp, themeable, Emitter ], mixins: [ withapp, themeable, asformitem() ],
inject: {
NFormItem: {
default: null
}
},
props: { props: {
type: { type: {
type: String, type: String,
@ -322,9 +317,6 @@ export default {
this.focus = false this.focus = false
this.triggerBlur(e) this.triggerBlur(e)
} }
if (this.NFormItem) {
this.dispatch('NFormItem', 'form-item-blur', e.target.value)
}
}, },
handleInputFocus (e) { handleInputFocus (e) {
this.$emit('input-focus') this.$emit('input-focus')
@ -347,9 +339,6 @@ export default {
this.$emit('keyup', e) this.$emit('keyup', e)
}, },
handleChange (e) { handleChange (e) {
if (this.NFormItem) {
this.dispatch('NFormItem', 'form-item-change', e.target.value)
}
this.$emit('change', e.target.value) this.$emit('change', e.target.value)
}, },
handleClick (e) { handleClick (e) {

View File

@ -0,0 +1,24 @@
export default function (events = {
change: 'change',
blur: 'blur',
focus: 'focus',
input: 'input'
}) {
return {
inject: {
NFormItem: {
default: null
}
},
created () {
Object.keys(events).forEach(event => {
const asEvent = events[event]
this.$on(event, function (value) {
if (this.NFormItem) {
this.NFormItem.$emit(asEvent, value)
}
})
})
}
}
}

View File

@ -6,13 +6,15 @@
font-size: 14px; font-size: 14px;
color: rgba(255, 255, 255, 1); color: rgba(255, 255, 255, 1);
@include m(inline) { @include m(inline) {
width: auto; width: 100%;
display: inline-flex; display: inline-flex;
align-items: flex-end; align-items: flex-end;
align-content: space-around; align-content: space-around;
flex-wrap: nowrap; @include b(form-item) {
.n-form-item:not(:last-child) { width: auto;
margin-right: 10px; &:last-child {
margin-right: 0;
}
} }
} }
.n-form-item__label--top { .n-form-item__label--top {
@ -71,21 +73,25 @@
height: 22px; height: 22px;
box-sizing: border-box; box-sizing: border-box;
font-size: 13px; font-size: 13px;
flex-grow: 0;
overflow-wrap: break-word;
@include e(span) {
display: inline-block;
}
} }
@include b(form-item-content) { @include b(form-item-content) {
position: relative; position: relative;
flex-grow: 1; flex-grow: 1;
} }
@include b(form-item-explain) { @include b(form-item-explain) {
min-height: 22px; box-sizing: border-box;
margin-top: 4px;
min-height: 20px;
line-height: 1.5; line-height: 1.5;
color: rgba(255, 146, 164, 1); color: rgba(255, 146, 164, 1);
font-size: 14px; font-size: 13px;
padding-left: 2px;
transform-origin: top left;
@include e(content) {
display: inline-block;
@include fade-down-transition($from-offset: -3px);
// @include fade-in-scale-up-transition();
}
} }
.n-form-item__content--pass { .n-form-item__content--pass {
.n-form-item__validate { .n-form-item__validate {

View File

@ -220,20 +220,37 @@ $default-cubic-bezier: cubic-bezier(.4, 0, .2, 1);
} }
} }
@mixin fade-down-transition ($name: 'fade-down', $from-offset: -4px) {
&.#{$namespace}-#{$name}-enter, &.#{$namespace}-#{$name}-leave-to {
opacity: 0;
transform: translateY($from-offset);
}
&.#{$namespace}-#{$name}-enter-to, &.#{$namespace}-#{$name}-leave {
opacity: 1;
transform: translateY(0);
}
&.#{$namespace}-#{$name}-leave-active {
transition: opacity .3s $default-cubic-bezier, transform .3s $default-cubic-bezier;
}
&.#{$namespace}-#{$name}-enter-active {
transition: opacity .3s $default-cubic-bezier, transform .3s $default-cubic-bezier;
}
}
@mixin fade-up-transition($block) { @mixin fade-up-transition($block) {
.#{$namespace}-#{$block}--transition-enter, .#{$namespace}-#{$block}--transition-leave-to { &.#{$namespace}-#{$block}--transition-enter, .#{$namespace}-#{$block}--transition-leave-to {
opacity: 0; opacity: 0;
transform: translateY(4px); transform: translateY(4px);
} }
.#{$namespace}-#{$block}--transition-leave-active { &.#{$namespace}-#{$block}--transition-leave-active {
transition: opacity .3s $slow-out-cubic-bezier, transform .3s $slow-out-cubic-bezier; transition: opacity .3s $slow-out-cubic-bezier, transform .3s $slow-out-cubic-bezier;
} }
.#{$namespace}-#{$block}--transition-enter-active { &.#{$namespace}-#{$block}--transition-enter-active {
transition: opacity .3s $fast-in-cubic-bezier, transform .3s $fast-in-cubic-bezier; transition: opacity .3s $fast-in-cubic-bezier, transform .3s $fast-in-cubic-bezier;
} }
} }
@mixin fade-in-scale-up-transition($block: 'fade-in-scale-up', $origin: inherit, $duration: .2s) { @mixin fade-in-scale-up-transition($block: 'fade-in-scale-up', $origin: inherit, $duration: .2s, $start-scale: 0.9) {
&.#{$namespace}-#{$block}--transition-leave-active { &.#{$namespace}-#{$block}--transition-leave-active {
transform-origin: $origin; transform-origin: $origin;
transition: opacity $duration $slow-out-cubic-bezier, transform $duration $slow-out-cubic-bezier; transition: opacity $duration $slow-out-cubic-bezier, transform $duration $slow-out-cubic-bezier;
@ -244,7 +261,7 @@ $default-cubic-bezier: cubic-bezier(.4, 0, .2, 1);
} }
&.#{$namespace}-#{$block}--transition-enter, &.#{$namespace}-#{$block}--transition-leave-to { &.#{$namespace}-#{$block}--transition-enter, &.#{$namespace}-#{$block}--transition-leave-to {
opacity: 0; opacity: 0;
transform: scale(.9); transform: scale($start-scale);
} }
&.#{$namespace}-#{$block}--transition-leave, &.#{$namespace}-#{$block}--transition-enter-to { &.#{$namespace}-#{$block}--transition-leave, &.#{$namespace}-#{$block}--transition-enter-to {
opacity: 1; opacity: 1;