1

I want to inject markup into existing components.

This is an easy example:

<!-- Arbitrary component X -->
<template>
  <div>
    <!-- I want a headline here -->
    foo!
  </div>
</template>

I know that I can achieve this by inserting a <slot/> and then using <X><h1>Hello world!</h1></X>. However, I want to do it dynamically without editing the original component.

So here's my idea using a higher-order-component:

import X from '~/components/X';
import injectHeadline from '~/hoc/injectHeadline.js';

export default {
  components: {
    X: injectHeadline(X, 'Hello world!')
  }
}

with

<!-- injectHeadline.js -->
export default (component, headline) => Vue.component({
    render(createElement) {
        let result = createElement(component);

        <!-- (*) somehow insert <h1>{{ headline }}</h1> here. How? -->

        return result;
    }
})

However, I had no luck manipulating the render result in (*). I tried fiddling with result.context.$children, but that leads nowhere. Any idea?

3
  • As a workaround, I found export default (component, title) => ({ extends: component, mounted() { let titleElement = document.createElement('h1'); titleElement.textContent = title; this.$el.insertBefore(titleElement, this.$el.firstChild); } });, but this works only on the client-side. I'd prefer the render phase. Commented Jun 21, 2019 at 6:36
  • Maybe have a look at Vue.compile function that compiles a template string into a render function. Commented Jun 21, 2019 at 6:44
  • @YomS. Thanks. I considered that. But I have trouble determining the original component's markup from within the render function. Commented Jun 21, 2019 at 6:52

2 Answers 2

2

It's possible to use the same approach for template extension as in React, to modify a hierarchy of VNodes before they are rendered to DOM, as explained in this answer:

export default (WrappedComponent, headline) => Vue.component({
  extends: WrappedComponent,
  render(h) {
    const elements = this.$options.extends.render.call(this, h);
    elements.children.unshift(<h1>{headline}</h1>));
    return elements;
  }
});
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks, exactly what I neded! For some reason it works only if I return a config object without Vue.component, so I ended up with export default (WrappedComponent, headline) => ({ extends: WrappedComponent, render(createElement) { const elements = this.$options.extends.render.call(this, createElement); elements.children.unshift(createElement('h1', headline)); return elements; } });
0

simple as that

<script>
export default {
  functional: true,
  render: function(createElement, context) {
    return createElement("div", context.slots().default);
  }
};
</script>

btw i set it to functional for performance (there is no need for vnode in this case)

in your code you could do it like this

let result = createElement(component,this.$slots.default);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.