Skip to content

Commit d04bcc7

Browse files
committed
Properly register props from mixins and extended components karol-f#31
2 parents ac5be50 + 19d6f8e commit d04bcc7

18 files changed

+1346
-1073
lines changed
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
<template>
2+
<div class="card card--primary">
3+
<el-table :data="tableData" style="width: 100%">
4+
<el-table-column prop="prop" label="Prop name" width="150">
5+
</el-table-column>
6+
<el-table-column prop="value" label="Value">
7+
</el-table-column>
8+
<el-table-column prop="type" label="typeof" width="100">
9+
</el-table-column>
10+
<el-table-column prop="default" label="default">
11+
</el-table-column>
12+
</el-table>
13+
</div>
14+
</template>
15+
16+
<script>
17+
export default {
18+
extends: {
19+
name: 'extended-class',
20+
props: ['fromExtended']
21+
},
22+
mixins: [{
23+
name: 'mixin-one',
24+
props: ['fromMixinOne']
25+
}, {
26+
props: {
27+
name: 'mixin-two',
28+
fromMixinOne: {
29+
type: String,
30+
default: () => 'mixin overrides mixin-prop'
31+
},
32+
fromExtended: {
33+
type: String,
34+
default: () => 'mixin overrides extended-prop'
35+
},
36+
fromMixinTwo: {
37+
type: String,
38+
default: () => 'mixin sets new prop'
39+
}
40+
}
41+
}],
42+
props: {
43+
fromMixinTwo: {
44+
type: String,
45+
default: () => 'instance overrides mixin-prop'
46+
}
47+
},
48+
methods: {
49+
getDefault(prop) {
50+
if (!this.$options.props[prop]) return '';
51+
const def = this.$options.props[prop].default;
52+
return (typeof def === 'function') ? def() : def;
53+
}
54+
},
55+
computed: {
56+
tableData() {
57+
return [{
58+
prop: 'fromMixinOne',
59+
value: JSON.stringify(this.fromMixinOne),
60+
type: typeof this.fromMixinOne,
61+
default: this.getDefault('fromMixinOne')
62+
}, {
63+
prop: 'fromMixinTwo',
64+
value: JSON.stringify(this.fromMixinTwo),
65+
type: typeof this.fromMixinTwo,
66+
default: this.getDefault('fromMixinTwo')
67+
}, {
68+
prop: 'fromExtended',
69+
value: JSON.stringify(this.fromExtended),
70+
type: typeof this.fromExtended,
71+
default: this.getDefault('fromExtended')
72+
}];
73+
}
74+
},
75+
created() {
76+
/* eslint-disable no-console */
77+
console.log('demo-inheritance created()');
78+
}
79+
};
80+
</script>
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
<template>
2+
<div>
3+
<h2>Inheritance demo</h2>
4+
5+
<div class="demo-card">
6+
<div class="el-form-item">
7+
<label class="el-form-item__label">fromMixinOne</label>
8+
<div class="el-form-item__content">
9+
<el-input v-model="compProps.fromMixinOne"></el-input>
10+
</div>
11+
</div>
12+
<demo-inheritance
13+
:from-mixin-one="compProps.fromMixinOne"
14+
></demo-inheritance>
15+
</div>
16+
17+
<el-collapse v-model="activeNames">
18+
<el-collapse-item title="Description" name="1">
19+
<p>Let's use <code>mixins</code> and extend the vue-component with <code>extends</code> to see the inheritance at work.</p>
20+
<p>All properties are displayed as expected and the declarative binding works properly.</p>
21+
<p>By taking a look at the default we can see that the inheritance-pattern common to vue has been honored.</p>
22+
<p>This is because the underlying vue instance handles the merging of options,
23+
while vue-custom-element only has to know all prop-keys in order to set the custom element attributes upon element-registration.
24+
</p>
25+
26+
</el-collapse-item>
27+
<el-collapse-item title="Custom Element HTML" name="2">
28+
<pre><code class="language-html">
29+
{{HTML}}
30+
</code></pre>
31+
</el-collapse-item>
32+
<el-collapse-item title="Vue component passed to Vue-custom-element" name="3">
33+
<pre><code class="language-html">
34+
&#x3C;template&#x3E;
35+
{{vueTemplate}}
36+
&#x3C;/template&#x3E;
37+
38+
&#x3C;script&#x3E;
39+
{{vueScript}}
40+
&#x3C;/script&#x3E;
41+
</code></pre>
42+
</el-collapse-item>
43+
</el-collapse>
44+
</div>
45+
</template>
46+
47+
<script>
48+
import Vue from 'vue';
49+
import DemoElement from 'demo/components/DemoInheritance-component';
50+
51+
export default {
52+
data() {
53+
return {
54+
message: 'Hello Vue!',
55+
activeNames: ['1'],
56+
compProps: {
57+
fromMixinOne: 'changed via attributes'
58+
},
59+
HTML: (
60+
`<demo-inheritance
61+
:from-mixin-one="fromMixinOne">
62+
</demo-inheritance>`
63+
),
64+
vueTemplate: (
65+
`<div class="card card--primary">
66+
<el-table :data="tableData" style="width: 100%">
67+
<el-table-column prop="prop" label="Prop name" width="150">
68+
</el-table-column>
69+
<el-table-column prop="value" label="Value">
70+
</el-table-column>
71+
<el-table-column prop="type" label="typeof" width="100">
72+
</el-table-column>
73+
<el-table-column prop="default" label="default">
74+
</el-table-column>
75+
</el-table>
76+
</div>`
77+
),
78+
vueScript: (
79+
`export default {
80+
extends: {
81+
name: 'extended-class',
82+
props: ['fromExtended']
83+
},
84+
mixins: [{
85+
name: 'mixin-one',
86+
props: ['fromMixinOne']
87+
}, {
88+
props: {
89+
name: 'mixin-two',
90+
fromMixinOne: {
91+
type: String,
92+
default: () => 'mixin overrides mixin-prop'
93+
},
94+
fromExtended: {
95+
type: String,
96+
default: () => 'mixin overrides extended-prop'
97+
},
98+
fromMixinTwo: {
99+
type: String,
100+
default: () => 'mixin sets new prop'
101+
}
102+
}
103+
}],
104+
props: {
105+
fromMixinTwo: {
106+
type: String,
107+
default: () => 'instance overrides mixin-prop'
108+
}
109+
},
110+
methods: {
111+
getDefault(prop) {
112+
if (!this.$options.props[prop]) return '';
113+
const def = this.$options.props[prop].default;
114+
return (typeof def === 'function') ? def() : def;
115+
}
116+
},
117+
computed: {
118+
tableData() {
119+
return [{
120+
prop: 'fromMixinOne',
121+
value: JSON.stringify(this.fromMixinOne),
122+
type: typeof this.fromMixinOne,
123+
default: this.getDefault('fromMixinOne')
124+
}, {
125+
prop: 'fromMixinTwo',
126+
value: JSON.stringify(this.fromMixinTwo),
127+
type: typeof this.fromMixinTwo,
128+
default: this.getDefault('fromMixinTwo')
129+
}, {
130+
prop: 'fromExtended',
131+
value: JSON.stringify(this.fromExtended),
132+
type: typeof this.fromExtended,
133+
default: this.getDefault('fromExtended')
134+
}];
135+
}
136+
},
137+
created() {
138+
/* eslint-disable no-console */
139+
console.log('demo-inheritance created()');
140+
}
141+
};`
142+
)
143+
};
144+
},
145+
methods: {
146+
registerCustomElement() {
147+
Vue.customElement('demo-inheritance', DemoElement);
148+
}
149+
}
150+
};
151+
</script>

demo/components/DemoLazyLoading-docs.vue

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@
2323

2424
<p>Instead of component object we will use function with returned Promise. You can use Webpack's <code>require.ensure()</code> or any other async method to load component.</p>
2525

26-
<p>One note - Custom Elements v1 spec require defining observed props during registration. That's why if you omit them, attributes won't be reactive, and changing them from outside (HTML attributes or JavaScript) won't work.</p>
26+
<p>One note - Custom Elements v1 spec require defining observed props during registration.
27+
That's why if you omit them, attributes won't be reactive, and changing them from outside (HTML attributes or JavaScript) won't work.
28+
This also concerns props added by <code>mixins</code> and <code>extends</code></p>
2729
</el-collapse-item>
2830
<el-collapse-item title="Custom Element HTML" name="2">
2931
<pre><code class="language-html">

demo/services/demos.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
export default {
22
basic: 'Basic',
33
binding: 'Binding',
4+
inheritance: 'Inheritance',
45
events: 'Events',
56
slots: 'Slots',
67
'lazy-loading': 'Lazy loading'

dist/vue-custom-element.esm.js

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -168,21 +168,34 @@ function getProps() {
168168
var componentDefinition = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
169169

170170
var props = {
171+
set: new Set(),
171172
camelCase: [],
172173
hyphenate: []
173174
};
174175

175-
if (componentDefinition.props && componentDefinition.props.length) {
176-
componentDefinition.props.forEach(function (prop) {
177-
props.camelCase.push(camelize(prop));
176+
if (componentDefinition.mixins) {
177+
componentDefinition.mixins.forEach(function (mixin) {
178+
if (!mixin.props) return;
179+
(Array.isArray(mixin.props) ? mixin.props : Object.keys(mixin.props)).forEach(function (propName) {
180+
props.set.add(propName);
181+
});
178182
});
179-
} else if (componentDefinition.props && _typeof(componentDefinition.props) === 'object') {
180-
for (var prop in componentDefinition.props) {
181-
props.camelCase.push(camelize(prop));
182-
}
183183
}
184+
if (componentDefinition.extends && componentDefinition.extends.props) {
185+
var parentProps = componentDefinition.extends.props;
184186

185-
props.camelCase.forEach(function (prop) {
187+
(Array.isArray(parentProps) ? parentProps : Object.keys(parentProps)).forEach(function (propName) {
188+
props.set.add(propName);
189+
});
190+
}
191+
if (componentDefinition.props) {
192+
var compProps = componentDefinition.props;
193+
(Array.isArray(compProps) ? compProps : Object.keys(compProps)).forEach(function (prop) {
194+
props.set.add(prop);
195+
});
196+
}
197+
Array.from(props.set).forEach(function (prop) {
198+
props.camelCase.push(camelize(prop));
186199
props.hyphenate.push(hyphenate(prop));
187200
});
188201

dist/vue-custom-element.js

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -174,21 +174,34 @@ function getProps() {
174174
var componentDefinition = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
175175

176176
var props = {
177+
set: new Set(),
177178
camelCase: [],
178179
hyphenate: []
179180
};
180181

181-
if (componentDefinition.props && componentDefinition.props.length) {
182-
componentDefinition.props.forEach(function (prop) {
183-
props.camelCase.push(camelize(prop));
182+
if (componentDefinition.mixins) {
183+
componentDefinition.mixins.forEach(function (mixin) {
184+
if (!mixin.props) return;
185+
(Array.isArray(mixin.props) ? mixin.props : Object.keys(mixin.props)).forEach(function (propName) {
186+
props.set.add(propName);
187+
});
184188
});
185-
} else if (componentDefinition.props && _typeof(componentDefinition.props) === 'object') {
186-
for (var prop in componentDefinition.props) {
187-
props.camelCase.push(camelize(prop));
188-
}
189189
}
190+
if (componentDefinition.extends && componentDefinition.extends.props) {
191+
var parentProps = componentDefinition.extends.props;
190192

191-
props.camelCase.forEach(function (prop) {
193+
(Array.isArray(parentProps) ? parentProps : Object.keys(parentProps)).forEach(function (propName) {
194+
props.set.add(propName);
195+
});
196+
}
197+
if (componentDefinition.props) {
198+
var compProps = componentDefinition.props;
199+
(Array.isArray(compProps) ? compProps : Object.keys(compProps)).forEach(function (prop) {
200+
props.set.add(prop);
201+
});
202+
}
203+
Array.from(props.set).forEach(function (prop) {
204+
props.camelCase.push(camelize(prop));
192205
props.hyphenate.push(hyphenate(prop));
193206
});
194207

dist/vue-custom-element.js.map

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)