1. 程式人生 > >Button按鈕--inject與provide

Button按鈕--inject與provide

required form oop img ice fine created label nested

inject 和 provider 是vue中的組合選項,需要一起使用。目的是允許一個祖先組件向其所有子孫後代註入依賴(簡單地說就是祖先組件向子孫後代傳值的一種方法,祖先組件通過provider提供變量,子孫後代通過inject註入接收變量)

provider: Object || () => Object

inject: Array || Object

Eg.

button.vue:

技術分享圖片
 1 <template>
 2   <button
 3     class="el-button"
 4     @click="handleClick"
 5     :disabled="buttonDisabled || loading"
 6
:autofocus="autofocus" 7 :type="nativeType" 8 :class="[ 9 type ? ‘el-button--‘ + type : ‘‘, 10 buttonSize ? ‘el-button--‘ + buttonSize : ‘‘, 11 { 12 ‘is-disabled‘: buttonDisabled, 13 ‘is-loading‘: loading, 14 ‘is-plain‘: plain, 15 ‘is-round‘: round,
16 ‘is-circle‘: circle 17 } 18 ]" 19 > 20 <i class="el-icon-loading" v-if="loading"></i> 21 <i :class="icon" v-if="icon && !loading"></i> 22 <span v-if="$slots.default"><slot></slot></span> 23 </button> 24 </template> 25
<script> 26 export default { 27 name: ‘ElButton‘, 28 29 // 通過inject向button中註入變量 30 inject: { 31 elForm: { 32 default: ‘‘ 33 }, 34 elFormItem: { 35 default: ‘‘ 36 } 37 }, 38 39 props: { 40 type: { 41 type: String, 42 default: ‘default‘ 43 }, 44 size: String, 45 icon: { 46 type: String, 47 default: ‘‘ 48 }, 49 nativeType: { 50 type: String, 51 default: ‘button‘ 52 }, 53 loading: Boolean, 54 disabled: Boolean, 55 plain: Boolean, 56 autofocus: Boolean, 57 round: Boolean, 58 circle: Boolean 59 }, 60 61 computed: { 62 _elFormItemSize() { 63 return (this.elFormItem || {}).elFormItemSize; 64 }, 65 buttonSize() { 66 return this.size || this._elFormItemSize || (this.$ELEMENT || {}).size; 67 }, 68 buttonDisabled() { 69 return this.disabled || (this.elForm || {}).disabled; // 通過inject註入的form變量,獲得祖先節點form的disabled屬性 70 } 71 }, 72 73 methods: { 74 handleClick(evt) { 75 this.$emit(‘click‘, evt); 76 } 77 } 78 }; 79 </script>
View Code

form.vue:

技術分享圖片
  1 <template>
  2   <form class="el-form" :class="[
  3     labelPosition ? ‘el-form--label-‘ + labelPosition : ‘‘,
  4     { ‘el-form--inline‘: inline }
  5   ]">
  6     <slot></slot>
  7   </form>
  8 </template>
  9 <script>
 10   import objectAssign from ‘element-ui/src/utils/merge‘;
 11 
 12   export default {
 13     name: ‘ElForm‘,
 14 
 15     componentName: ‘ElForm‘,
 16 
 17     // 通過provider向子孫後代註入變量elform,講this(即form)註入給子孫後代,後代通過獲取此變量獲取form中的各種配置,如disabled屬性等
 18     provide() {
 19       return {
 20         elForm: this
 21       };
 22     },
 23 
 24     props: {
 25       model: Object,
 26       rules: Object,
 27       labelPosition: String,
 28       labelWidth: String,
 29       labelSuffix: {
 30         type: String,
 31         default: ‘‘
 32       },
 33       inline: Boolean,
 34       inlineMessage: Boolean,
 35       statusIcon: Boolean,
 36       showMessage: {
 37         type: Boolean,
 38         default: true
 39       },
 40       size: String,
 41       disabled: Boolean,
 42       validateOnRuleChange: {
 43         type: Boolean,
 44         default: true
 45       },
 46       hideRequiredAsterisk: {
 47         type: Boolean,
 48         default: false
 49       }
 50     },
 51     watch: {
 52       rules() {
 53         if (this.validateOnRuleChange) {
 54           this.validate(() => {});
 55         }
 56       }
 57     },
 58     data() {
 59       return {
 60         fields: []
 61       };
 62     },
 63     created() {
 64       this.$on(‘el.form.addField‘, (field) => {
 65         if (field) {
 66           this.fields.push(field);
 67         }
 68       });
 69       /* istanbul ignore next */
 70       this.$on(‘el.form.removeField‘, (field) => {
 71         if (field.prop) {
 72           this.fields.splice(this.fields.indexOf(field), 1);
 73         }
 74       });
 75     },
 76     methods: {
 77       resetFields() {
 78         if (!this.model) {
 79           process.env.NODE_ENV !== ‘production‘ &&
 80           console.warn(‘[Element Warn][Form]model is required for resetFields to work.‘);
 81           return;
 82         }
 83         this.fields.forEach(field => {
 84           field.resetField();
 85         });
 86       },
 87       clearValidate(props = []) {
 88         const fields = props.length
 89           ? this.fields.filter(field => props.indexOf(field.prop) > -1)
 90           : this.fields;
 91         fields.forEach(field => {
 92           field.clearValidate();
 93         });
 94       },
 95       validate(callback) {
 96         if (!this.model) {
 97           console.warn(‘[Element Warn][Form]model is required for validate to work!‘);
 98           return;
 99         }
100 
101         let promise;
102         // if no callback, return promise
103         if (typeof callback !== ‘function‘ && window.Promise) {
104           promise = new window.Promise((resolve, reject) => {
105             callback = function(valid) {
106               valid ? resolve(valid) : reject(valid);
107             };
108           });
109         }
110 
111         let valid = true;
112         let count = 0;
113         // 如果需要驗證的fields為空,調用驗證時立刻返回callback
114         if (this.fields.length === 0 && callback) {
115           callback(true);
116         }
117         let invalidFields = {};
118         this.fields.forEach(field => {
119           field.validate(‘‘, (message, field) => {
120             if (message) {
121               valid = false;
122             }
123             invalidFields = objectAssign({}, invalidFields, field);
124             if (typeof callback === ‘function‘ && ++count === this.fields.length) {
125               callback(valid, invalidFields);
126             }
127           });
128         });
129 
130         if (promise) {
131           return promise;
132         }
133       },
134       validateField(prop, cb) {
135         let field = this.fields.filter(field => field.prop === prop)[0];
136         if (!field) { throw new Error(‘must call validateField with valid prop string!‘); }
137 
138         field.validate(‘‘, cb);
139       }
140     }
141   };
142 </script>
View Code

form-item.vue

技術分享圖片
  1 <template>
  2   <div class="el-form-item" :class="[{
  3       ‘el-form-item--feedback‘: elForm && elForm.statusIcon,
  4       ‘is-error‘: validateState === ‘error‘,
  5       ‘is-validating‘: validateState === ‘validating‘,
  6       ‘is-success‘: validateState === ‘success‘,
  7       ‘is-required‘: isRequired || required,
  8       ‘is-no-asterisk‘: elForm && elForm.hideRequiredAsterisk
  9     },
 10     sizeClass ? ‘el-form-item--‘ + sizeClass : ‘‘
 11   ]">
 12     <label :for="labelFor" class="el-form-item__label" :style="labelStyle" v-if="label || $slots.label">
 13       <slot name="label">{{label + form.labelSuffix}}</slot>
 14     </label>
 15     <div class="el-form-item__content" :style="contentStyle">
 16       <slot></slot>
 17       <transition name="el-zoom-in-top">
 18         <slot 
 19           v-if="validateState === ‘error‘ && showMessage && form.showMessage" 
 20           name="error" 
 21           :error="validateMessage">
 22           <div
 23             class="el-form-item__error"
 24             :class="{
 25               ‘el-form-item__error--inline‘: typeof inlineMessage === ‘boolean‘
 26                 ? inlineMessage
 27                 : (elForm && elForm.inlineMessage || false)
 28             }"
 29           >
 30             {{validateMessage}}
 31           </div>
 32         </slot>
 33       </transition>
 34     </div>
 35   </div>
 36 </template>
 37 <script>
 38   import AsyncValidator from ‘async-validator‘;
 39   import emitter from ‘element-ui/src/mixins/emitter‘;
 40   import objectAssign from ‘element-ui/src/utils/merge‘;
 41   import { noop, getPropByPath } from ‘element-ui/src/utils/util‘;
 42 
 43   export default {
 44     name: ‘ElFormItem‘,
 45 
 46     componentName: ‘ElFormItem‘,
 47 
 48     mixins: [emitter],
 49 
 50     // 通過provider向子孫後代註入變量elform,講this(即form-item)註入給子孫後代,後代通過獲取此變量獲取form中的各種配置,如size屬性等
 51     provide() {
 52       return {
 53         elFormItem: this
 54       };
 55     },
 56 
 57     inject: [‘elForm‘],
 58 
 59     props: {
 60       label: String,
 61       labelWidth: String,
 62       prop: String,
 63       required: {
 64         type: Boolean,
 65         default: undefined
 66       },
 67       rules: [Object, Array],
 68       error: String,
 69       validateStatus: String,
 70       for: String,
 71       inlineMessage: {
 72         type: [String, Boolean],
 73         default: ‘‘
 74       },
 75       showMessage: {
 76         type: Boolean,
 77         default: true
 78       },
 79       size: String
 80     },
 81     watch: {
 82       error: {
 83         immediate: true,
 84         handler(value) {
 85           this.validateMessage = value;
 86           this.validateState = value ? ‘error‘ : ‘‘;
 87         }
 88       },
 89       validateStatus(value) {
 90         this.validateState = value;
 91       }
 92     },
 93     computed: {
 94       labelFor() {
 95         return this.for || this.prop;
 96       },
 97       labelStyle() {
 98         const ret = {};
 99         if (this.form.labelPosition === ‘top‘) return ret;
100         const labelWidth = this.labelWidth || this.form.labelWidth;
101         if (labelWidth) {
102           ret.width = labelWidth;
103         }
104         return ret;
105       },
106       contentStyle() {
107         const ret = {};
108         const label = this.label;
109         if (this.form.labelPosition === ‘top‘ || this.form.inline) return ret;
110         if (!label && !this.labelWidth && this.isNested) return ret;
111         const labelWidth = this.labelWidth || this.form.labelWidth;
112         if (labelWidth) {
113           ret.marginLeft = labelWidth;
114         }
115         return ret;
116       },
117       form() {
118         let parent = this.$parent;
119         let parentName = parent.$options.componentName;
120         while (parentName !== ‘ElForm‘) {
121           if (parentName === ‘ElFormItem‘) {
122             this.isNested = true;
123           }
124           parent = parent.$parent;
125           parentName = parent.$options.componentName;
126         }
127         return parent;
128       },
129       fieldValue() {
130         const model = this.form.model;
131         if (!model || !this.prop) { return; }
132 
133         let path = this.prop;
134         if (path.indexOf(‘:‘) !== -1) {
135           path = path.replace(/:/, ‘.‘);
136         }
137 
138         return getPropByPath(model, path, true).v;
139       },
140       isRequired() {
141         let rules = this.getRules();
142         let isRequired = false;
143 
144         if (rules && rules.length) {
145           rules.every(rule => {
146             if (rule.required) {
147               isRequired = true;
148               return false;
149             }
150             return true;
151           });
152         }
153         return isRequired;
154       },
155       _formSize() {
156         return this.elForm.size;
157       },
158       elFormItemSize() {
159         return this.size || this._formSize;
160       },
161       sizeClass() {
162         return this.elFormItemSize || (this.$ELEMENT || {}).size;
163       }
164     },
165     data() {
166       return {
167         validateState: ‘‘,
168         validateMessage: ‘‘,
169         validateDisabled: false,
170         validator: {},
171         isNested: false
172       };
173     },
174     methods: {
175       validate(trigger, callback = noop) {
176         this.validateDisabled = false;
177         const rules = this.getFilteredRule(trigger);
178         if ((!rules || rules.length === 0) && this.required === undefined) {
179           callback();
180           return true;
181         }
182 
183         this.validateState = ‘validating‘;
184 
185         const descriptor = {};
186         if (rules && rules.length > 0) {
187           rules.forEach(rule => {
188             delete rule.trigger;
189           });
190         }
191         descriptor[this.prop] = rules;
192 
193         const validator = new AsyncValidator(descriptor);
194         const model = {};
195 
196         model[this.prop] = this.fieldValue;
197 
198         validator.validate(model, { firstFields: true }, (errors, invalidFields) => {
199           this.validateState = !errors ? ‘success‘ : ‘error‘;
200           this.validateMessage = errors ? errors[0].message : ‘‘;
201 
202           callback(this.validateMessage, invalidFields);
203           this.elForm && this.elForm.$emit(‘validate‘, this.prop, !errors, this.validateMessage || null);
204         });
205       },
206       clearValidate() {
207         this.validateState = ‘‘;
208         this.validateMessage = ‘‘;
209         this.validateDisabled = false;
210       },
211       resetField() {
212         this.validateState = ‘‘;
213         this.validateMessage = ‘‘;
214 
215         let model = this.form.model;
216         let value = this.fieldValue;
217         let path = this.prop;
218         if (path.indexOf(‘:‘) !== -1) {
219           path = path.replace(/:/, ‘.‘);
220         }
221 
222         let prop = getPropByPath(model, path, true);
223 
224         this.validateDisabled = true;
225         if (Array.isArray(value)) {
226           prop.o[prop.k] = [].concat(this.initialValue);
227         } else {
228           prop.o[prop.k] = this.initialValue;
229         }
230 
231         this.broadcast(‘ElTimeSelect‘, ‘fieldReset‘, this.initialValue);
232       },
233       getRules() {
234         let formRules = this.form.rules;
235         const selfRules = this.rules;
236         const requiredRule = this.required !== undefined ? { required: !!this.required } : [];
237 
238         const prop = getPropByPath(formRules, this.prop || ‘‘);
239         formRules = formRules ? (prop.o[this.prop || ‘‘] || prop.v) : [];
240 
241         return [].concat(selfRules || formRules || []).concat(requiredRule);
242       },
243       getFilteredRule(trigger) {
244         const rules = this.getRules();
245 
246         return rules.filter(rule => {
247           if (!rule.trigger || trigger === ‘‘) return true;
248           if (Array.isArray(rule.trigger)) {
249             return rule.trigger.indexOf(trigger) > -1;
250           } else {
251             return rule.trigger === trigger;
252           }
253         }).map(rule => objectAssign({}, rule));
254       },
255       onFieldBlur() {
256         this.validate(‘blur‘);
257       },
258       onFieldChange() {
259         if (this.validateDisabled) {
260           this.validateDisabled = false;
261           return;
262         }
263 
264         this.validate(‘change‘);
265       }
266     },
267     mounted() {
268       if (this.prop) {
269         this.dispatch(‘ElForm‘, ‘el.form.addField‘, [this]);
270 
271         let initialValue = this.fieldValue;
272         if (Array.isArray(initialValue)) {
273           initialValue = [].concat(initialValue);
274         }
275         Object.defineProperty(this, ‘initialValue‘, {
276           value: initialValue
277         });
278 
279         let rules = this.getRules();
280 
281         if (rules.length || this.required !== undefined) {
282           this.$on(‘el.form.blur‘, this.onFieldBlur);
283           this.$on(‘el.form.change‘, this.onFieldChange);
284         }
285       }
286     },
287     beforeDestroy() {
288       this.dispatch(‘ElForm‘, ‘el.form.removeField‘, [this]);
289     }
290   };
291 </script>
View Code

Button按鈕--inject與provide