In the last section, we laid the technical groundwork for component development. In this section, we will enter the actual field
Let’s start by implementing a set of Form components. If you’ve used any of the popular component libraries on the market, you should know that a set of Form components should contain something like this
So let’s start with the next use case, starting with the use case, and working backwards with the components
<f-form :model="userForm" :rules="rules">
<f-form-item label="Username" prop="username">
<f-input v-model="userForm.username" placeholder="Please enter user name" />
</f-form-item>
<f-form-item label="Password" prop="password">
<f-input type="password" v-model="userForm.password" placeholder="Please enter your password" />
</f-form-item>
</f-form>
<script>
export default {
data() {
return {
userForm: {
username: ' '.password: ' '
},
rules: {
username: [{required: true.message: 'Please enter a user name'}].password: [{required: true.message: 'Please enter your password'}]}}}}</script>
Copy the code
Implement it from the inside out, starting with input, then form-item, and then form, without further ado
f-input
Start with the input field. You might think it’s simple, but remember we said earlier that child components can’t directly modify the values that prop receives, so throw out your idea of v-model binding directly to prop.
If you don’t turn off alerts you’ll get warnings like this
You need to know that v-model is a syntactic sugar, and these two lines actually do the same thing
1<input v-model="username" />
<br/>
2<input :value="username" @input="username = $event.target.value" />
<br/>
{{username}}
Copy the code
Let’s run it and see what happens
So in F-input you can do this for binding passing
<template>
<div>
<input :value="value" @input="inputHandler" />
</div>
</template>
<script>
export default {
props: {
value: {
type: String.default: ' '}},methods: {
inputHandler(e) {
this.$emit('input', e.target.value)
}
}
}
</script>
Copy the code
At this time to try again, found that it is already ok, the console did not report a warning
And then you see a problem. The placeholder setting isn’t in effect because it’s being added to the F-Input instead of the input inside the F-Input
There is a paragraph in the API documentation on the official website
If this option is enabled, attributes bound to the parent component that are not accepted by props will be stored in attrs. You can use v−bind to attrs. You can use v−bind to attrs on any non-root component. Then we’ll tweak our F-input a little bit
<template>
<div>
<input :value="value" @input="inputHandler" v-bind="$attrs" />
</div>
</template>
<script>
export default {
inheritAttrs: true.// Omit some code
</script>
Copy the code
Perfect. You can see that the password input box has been set up
f-form-item
This is a little bit more difficult than the input box up here, but it’s pretty easy, and the point that I need to achieve here is
- The label text
- Reserve slots for form components
- Check and error according to rules
The first two are easy, but you should be more afraid of the third one. There is a ready-made “wheel” called Async-Validator. As a programmer, there is no need to duplicate wheels 😂
Think about the form-item attributes of our previous use case: label and prop, and then configure the parameters to receive
<template>
<div>
<label v-if="label">{{label}}</label>
<slot></slot>
<p v-if="errorMessage">{{errorMessage}}</p>
</div>
</template>
<script>
export default {
props: {
label: {
type: String,
default: ''
},
prop: {
type: String,
}
},
data() {
return {
errorMessage: ''
}
},
}
</script>
Copy the code
With step 1 and Step 2 complete, validate is the next step. The prop is used to get the validation rule from the rules bound to the form component and the value of the current form item from the form bound content. Here you can use provide/inject to pass data
<script> import Schema from 'async-validator' export default { inject: ['form'], props: { label: { type: String, default: '' }, prop: { type: String, } }, data() { return { errorMessage: '' } }, methods: {validate() {const rule = this.form.rules[this.prop] const value = this.form.model[this.prop] // Get the validation rule instance const description = {[this.prop]: Rule} const schema = new schema (description) return schema.validate({[this.prop]: Value}, (error, field) => {if(error) {this.errorMessage = error[0].message console.log(' ${field} failed '); } else { this.errorMessage = '' } }) } }, } </script>Copy the code
Then add a listener that validates the event dynamically when the internal form item value changes
mounted() {
this.$on('validate', this.validate)
}
Copy the code
Modify the f-input content as well
Methods: {inputHandler(e) {this.$emit('input', e.target.value)}}Copy the code
OK ~ ~ form – item together live
f-form
After completing the Form-item and input components, the Form component is simple. Let’s take a look at what the form does
- Receive validation rules and form data
- Reserve slots for form-items
- Global check method
First we implement the first two steps. It’s very simple. Don’t forget that the form-item uses inject data, which needs to be bound
<template>
<div>
<slot></slot>
</div>
</template>
<script>
export default {
provide() {
return {
form: this
}
},
props: {
model: {
type: Object,
required: true
},
rules: {
type: Object
}
}
}
</script>
Copy the code
Next, the global validation takes a callback that takes a Boolean to indicate whether the validation passes, uses promise. all to execute multiple validators, and returns false if any fail
methods: {
validate(cb) {
const validators = this.$children
.filter(item= > item.prop)
.map(item= > item.validate())
Promise.all(validators)
.then(() = > cb(true))
.catch(() = > cb(false}})),Copy the code
Then modify the use case by adding a submit button and binding global validation
<template> <div> < F-form :model="userForm" :rules="rules" ref="formRef"> < F-form-item label=" prop "="username"> <f-input v-model=" userform. username" placeholder=" please input username" /> </f-form-item> <f-form-item label=" password" ="password"> <f-input type="password" v-model=" userform. password" placeholder=" please input password" /> </f-form-item> <button </ form> </div> </template> </ script> {submit() {this.$refs.formref.validate (valid => {if(valid) {console.log(' validated '); } else {console.log(' verification failed '); } }) } }, } </script>Copy the code
So far, we have roughly completed a set of form components. Of course, there is more to them than that. This is just a start, but I won’t go into details here
This article is part of the “Gold Nuggets For Free!” Event, click to view details of the event