首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Button按钮--inject与provide

Button按钮--inject与provide

作者头像
用户1148399
发布2018-10-11 14:35:10
1.1K0
发布2018-10-11 14:35:10
举报
文章被收录于专栏:web前端web前端

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>

 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>

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>
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018-10-06 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档