Skip to content

Instantly share code, notes, and snippets.

@isometriq
Created February 24, 2018 04:15
Show Gist options
  • Save isometriq/8843fa3011fd40c3ef0e83870de0430c to your computer and use it in GitHub Desktop.
Save isometriq/8843fa3011fd40c3ef0e83870de0430c to your computer and use it in GitHub Desktop.
Extending a Vue component template
function isObject(value) {
return value !== null && value !== undefined && typeof value === 'object';
}
function isArray(value) {
return value && value.constructor === Array;
}
function isString(value) {
return typeof value === 'string';
}
// Replace the slot default content of a component
function extendTemplate(template, slots)
{
Object.keys(slots).map((slotName, index) =>
{
let regexPattern = slotName=='default' ? '<slot[\s\S]*>' : '<slot(.+name="('+slotName+')")?[\s\S]*>'
let regex = new RegExp(regexPattern, 'ig');
let results = [];
let result = null;
while ((result = regex.exec(template)) !== null) {
results.push(result);
}
if (!results.length) {
return false;
}
results.map((result, index) => {
if (slotName=='default' || (result[2]!=null && result[2]==slotName))
{
let start = result.index + result[0].length;
let end = result.input.indexOf('</slot>', start);
if (end == -1) {
return;
}
template = template.substring(0, start) + slots[slotName] + template.substring(end);
}
});
});
return template;
}
// Detect if extending and try to apply template extension
function extendComponent(component)
{
if (isObject(component.extends) && isString(component.extends.template) && (isObject(component.template) || isString(component.template)))
{
if (isString(component.template)) {
component.template = { default: component.template };
}
component.template = extendTemplate(component.extends.template, component.template);
// Merge components as they can be needed in the extended template
if (isObject(component.components) && isObject(component.extends.components)) {
component.components = Object.assign(component.components, component.extends.components);
}
component.computed = Object.assign({}, component.computed ? component.computed : {}, {
super() {
return this.constructor.options.extends;
}
});
}
return component;
}
// Component do-it-all decorator
function component(component) {
return extendComponent(component);
}
export default component
export { component, extendComponent, extendTemplate }
import component from './component'
export default component({
name: 'form',
template: `
<div>
<div>
<slot name="header"><h2>{{ title }}</h2></slot>
</div>
<div>
<slot name="fields"></slot>
</div>
<div>
<!-- save cancel buttons, etc... -->
</div>
</div>
`,
props: {
title: String
},
data: ()=>({
form: {
data: {}
}
}),
methods: {
reset() {
// clear form data etc..
}
submit() {
// send form data etc..
}
}
})
import Form from './Form'
export default component({
name: 'user-form',
extends: Form,
template: {
`fields`: `
<p>Name: <input type="text" v-model="form.data.name" /></p>
<p>Email: <input type="text" v-model="form.data.email" /></p>
`
},
props: {
title: {default: 'User Form'}
},
methods: {
userSpecific() {
// user specific logic etc..
}
}
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment