专栏首页web前端Button按钮--inject与provide

Button按钮--inject与provide

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>

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 拼多多、趣头条上市,反驳消费升级的无效样本

    最近,有关消费升级与消费降级的讨论一直都没有停止过。作为一个新的风口和方向,人们对于消费升级和消费降级的谈论无可厚非,因为只有深入讨论才能找到合适的发展方向。但...

    孟永辉
  • 锤子手机推“无限屏”新机:让我们为老罗的创新点赞

    人们对于锤子手机的发布会的期待其实更多的是在看罗永浩的“单口相声”。无论锤子手机如何,观众依然能够在锤子手机发布会上过一回嘴瘾。罗永浩对于自己,对于友商,对于行...

    孟永辉
  • 任性的苹果,任性的库克

    或许在人类历史上还没有哪一个科技公司会像苹果般任性,任性到可以不顾及用户的感受。随着苹果发布会的新鲜“上线”,我们再次见识到了苹果的任性。价格再度刷新史上最高、...

    孟永辉
  • 马云退休,一个全新的互联网时代或将开始

    今天,马云退休的消息无疑是一个焦点。对于马云退休的原因,外界依然有很多的猜测。几乎可以确定的是,未来马云将会从事自己心仪的教育事业,并将会持续发挥自己在教育事业...

    孟永辉
  • 饿了么并入口碑,一场“非典型”新零售大战的开始

    如果你了解互联网巨头的套路,你就会发现他们会不断用资本的力量去拓展自己的势力范围,并且建构一个能够将人们所有的生活场景全部都囊括其中的生态体系。饿了么并入口碑仅...

    孟永辉
  • 1万步21天钉钉运动大神赛

      目前所在的企业,从入职开始到现在,内部沟通协作的主要软件也就是钉钉。三月初的时候,偶然在钉钉运动一栏中发现了这个活动,当时也没怎么犹豫,果断支付了2元契约金...

    happyJared
  • BAT、TMD加持新技术背景下,互联网家装如何蝶变新生?

    几乎所有的“互联网+”模式都存在同样的问题,那就是对于行业内在运行逻辑改造的无力与浅层。互联网家装同样如此。我们看到,尽管互联网家装改变了人们获取用户的方式和手...

    孟永辉
  • 或许,9月12日的苹果新品发布会依然会让人失望!

    正如每一次的苹果邀请函都带给我们不一样的感受一样,今年9月12日的苹果发布会的邀请函依然让我们浮想联翩。这或许是苹果以往营销套路的延续——通过给人一个极具想象力...

    孟永辉
  • 恒大入主法拉第,FF91真的要来了吗?

    自恒大入主法拉第之后,FF91落地的步伐似乎开始逐步加快。在许家印到法拉第公司考察之后,FF91在落地到中国的步伐更加快速。就在上周,恒大法拉第未来智能汽车集团...

    孟永辉
  • 共享单车落幕:上海凤凰诉ofo或在情理之中

    共享单车浩浩荡荡的发展历程伴随着资本的退却而最终落幕。尽管共享单车市场上摩拜投身美团总算是有了一个好归宿,哈罗单车对于三四线的深度布局实现了逆袭,ofo依然在仗...

    孟永辉

扫码关注云+社区

领取腾讯云代金券