How to get Vue 3 render function h() to output html - vue.js

I am using Vue 3 render function and cannot work out how to render HTML in the template.
I have tried this:
h('span', {}, '<strong>Bold</strong>');
But it outputs the html tags <strong>Bold</strong> on the page rather than Bold.
Does anyone know how to get the render function to render HTML?

According to h() arguments,I think the render function could not render HTML directly in the children argument, you need to transfer to another render function like
return h('span', {}, [h('strong', 'Bold')]);
But you could set innerHTML in the props argument to make it:
return h('span', {
innerHTML:'<strong>Bold</strong>'
},[]);

Related

How to wait for variable in window object before render a Vue component?

I have a component that need to access variable inside window object. But it renders before it loaded. So is there any solution delay that component render until variable will exists?
you can try to wrap it into a
this.$nextTick(() => {
// ur code here
})
nextTick is a function which waits till the dom is complete loaded and executes the content inside.

VueJS Use VNode inside render function with createElement?

I am trying to render a Vnode inside my render function. In such a way that I can still give that element children.
I know you can have an array of Vnodes as the third argument of createElement(tag, data, vnode[]) but since I want to give this specific Vnode children still I seem to be a little stuck,
I have tried doing something like:
const vnodeObj = {tag: vnode.tag, data: vnode.data}
//Skip to inside render function
createElement(vnodeObj.tag, vnodeObj.data, []).
Which worked well to create the tag but I have found that the data object returend from Vnode.data is not the same for the data object createElement expect for example:
If I were to add a class "test" and a ref "test2" to a element using create element the data object would look like so:
{
attrs: { class: "test", ref: "test2"},
}
while the Vnode.data would return something like:
{
"ref":"test2",
"staticClass":"test"
}
Leading me to believe there must be a better way to render a single Vnode and its data and continue giving it children with createElement.

Vuejs:Why is render method returning dynamic `<component>` giving errors?

I am trying to return vue's builtin <component/> from the render method, but it is not recognizing the component. what could be the possible mistake?
Vue.component('comp1',{
template:'<h1>Component1</h1>'
});
Vue.component('comp2',{
template:'<h1>Component2</h1>'
});
let app=new Vue({
el:'#app',
data:function(){
return {
comp:'comp1'
}
},
mounted:function(){
setInterval(()=>{
if(this.comp=='comp1'){
this.comp="comp2"
}else{
this.comp="comp1"
}
},1000);
},
render (h) {
return h('component', this.comp)
}
})
linkto js bin: https://jsbin.com/wakivet/edit?html,js,console,output
Replace:
render(h){
return h('component',this.comp);
}
with
template: '<component :is="comp" />'
There is no built-in component called component in Vue.
Vue needed a syntax for creating dynamic components in a template. It also needed a way to solve the problems caused by in-DOM templates requiring specific elements as children of other elements. Both of these problems could be solved the same way, with the introduction of the is attribute.
But even though the desired tag name is specified via the is attribute we still need to include a tag name in the template. Any tag will work, but the convention is to use the tag <component>.
<component :is="comp">
Using this convention allows other developers to know that the tag is dynamic. It also makes it easier for the Vue template compiler to optimise the template.
Vue will log a warning if anyone tries to register a global component called component, just like it would for other reserved tag names. This helps to ensure there's no ambiguity when those tags are used.
However, it is just a convention. Using other tags will work just fine and with in-DOM templates it is sometimes necessary, see https://v2.vuejs.org/v2/guide/components.html#DOM-Template-Parsing-Caveats.
But what about render functions?
The first argument passed to the function h is the tag name. We don't need to use tricks like the is attribute to make that dynamic, we can just pass it directly:
h(this.comp)
In fact, if you write a template containing <component :is="comp"> then Vue will compile it down to something very similar to this. The compiled render function doesn't keep the is attribute, it throws that away and just passes its value to the h function.
You can see this for yourself if you inspect a compiled template. For example, you can use the tool at https://v2.vuejs.org/v2/guide/render-function.html#Template-Compilation to see how templates are compiled to render functions. The function _c is what you called h.
So to reiterate, there is no such component as <component>. It is merely a placeholder in template syntax for some other component. When you're in a render function you should just use the desired component directly.

How can I duplicate slots within a Vuejs render function?

I have a component which is passed content via a slot. I'm using a render function to output the content. The reason I'm using a render function is because I want to duplicate the content multiple times. When I use this code, everything works fine:
render(createElement){
return createElement('div', {}, this.$slots.default);
}
When I data that is being passed changes, the output changes as well.
However, since I want to duplicate the slot content, I'm now trying this:
return createElement(
'div', {},
[
createElement('div', { }, this.$slots.default),
createElement('div', { }, this.$slots.default)
]
)
Now the problem is, when the slot content changes from outside the component, only the content in the second div gets updated, the content in the first div stays the same..
Am I missing something here?
I can't explain why it happens. But the doc does mention that "VNodes Must Be Unique" in a render function. See https://v2.vuejs.org/v2/guide/render-function.html#Constraints.
Anyway, this is a VNode cloning function, which works, which I discovered from https://jingsam.github.io/2017/03/08/vnode-deep-clone.html.
function deepClone(vnodes, createElement) {
function cloneVNode(vnode) {
const clonedChildren = vnode.children && vnode
.children
.map(vnode => cloneVNode(vnode));
const cloned = createElement(vnode.tag, vnode.data, clonedChildren);
cloned.text = vnode.text;
cloned.isComment = vnode.isComment;
cloned.componentOptions = vnode.componentOptions;
cloned.elm = vnode.elm;
cloned.context = vnode.context;
cloned.ns = vnode.ns;
cloned.isStatic = vnode.isStatic;
cloned.key = vnode.key;
return cloned;
}
const clonedVNodes = vnodes.map(vnode => cloneVNode(vnode))
return clonedVNodes;
}
How to use it:
render(createElement) {
return createElement('div', {}, [
createElement('div', {}, this.$slots.default),
createElement('div', {}, [...deepClone(this.$slots.default, createElement)])
])
}
Demo: https://jsfiddle.net/jacobgoh101/bz3e0o5m/
I found this SO question searching for a way to render the content of a slot multiple times like e.g. for a generic list that can have a template for the content of a list row, which is used for each item.
As of 2020 (in fact earlier) multiple rendering of a slot can be achieved using scoped slots. This is documented here:
https://v2.vuejs.org/v2/guide/components-slots.html#Other-Examples
The documentation says:
Slot props allow us to turn slots into reusable templates that can render different content based on input props
(obviously, if we can use the template to render different content based on props, we can also use it to render the same content)
The example given right there uses a template instead of a render function, but how to use scoped slots in a render function is fortunately also documented:
https://v2.vuejs.org/v2/guide/render-function.html#Slots

Vuejs: How to trigger render function?

I currently have a component with a render function, to which I send data using a slot.
In short my component looks as follows:
export default {
render(createElement){
return createElement(
'div', {
'class' : 'className'
},
this.$slots.default
)
}
}
There is a bit more inside the render function that creates multiple elements and puts the slot content in each of the element (which is the reason I'm using a render function), but that's not relevant for this example.
I have another component which has this template:
<Component1>
<div>foo</div>
</Component1>
(component 1 being the component with the render function).
This all works nicely, but the problem is that when the word 'foo' changes, the component doesn't get updated. I can send a prop to the component to check wether the content gets changed (by putting a watcher on the prop), but how can I force the component to run the render function again?
Thanks!