Vue add content inside #document-fragment - vue.js

I'm using vue3 with nuxt3 and want to add a template tag with content inside the generated #document-fragment. The resulting HTML should look like this:
<body>
<template id="some-id">
#document-fragment
<div>
...
</div>
</template>
</body>
when I use plain html, that works great. With vue3 the elements are not inside the #document-fragment but below it like this:
<body>
<template id="some-id">
#document-fragment
<div>
...
</div>
</template>
</body>
My vue3 code looks like this (similar to the html code):
<template>
<v-app>
<template id="some-id">
<div></div>
</template>
</v-app>
</template>
is there any way to put the content inside the #document-fragment element?

I solved it by using a ref on the template tag and then the render-function.
<template id="some-id" ref="someRef">
</template>
const someRef: Ref<HTMLTemplateElement | undefined> = ref()
onMounted(() => {
if (someRef.value?.content) {
// #ts-ignore
render('div', someRef.value.content)
}
})
if you want to insert a component you can use the it like this:
const someRef: Ref<HTMLTemplateElement | undefined> = ref()
onMounted(() => {
if (someRef.value?.content) {
// #ts-ignore
render(h(SomeComponent, { someProp: someValue }), someRef.value.content)
}
})
Maybe there is a better, but for now that works.

Related

How to use v-model using props in v-for in Vue 3

I want to use v-model using props in v-for. I used v-model with props before using computed variables, but don't know how to do in v-for.
This is my code:
<script setup>
const fieldValue = computed({
get() {
return props.modelValue;
},
set(value) {
emits('update:modelValue', value);
},
});
</script>
<template>
<Mentionable
v-for="(field, index) in props.data.placeholder"
:key="index"
:keys="['#']"
:items="props.data.items"
>
<textarea
v-model="fieldValue"
:disabled="!props.data.loaded"
:placeholder="field.placeholder"
:maxlength="props.maxLength"
:required="props.required"
/>
</Mentionable>
</template>
Note: Mentionable is the component from vue-mention library.
Use model-value and update:model-value event instend of v-model.
If the component does not provide model-value and update:model-value event, there must be prop and event you can do the same.
A example, AInput from Ant Design Vue provides value prop and change event.
<script setup name="Demo">
const {ref} from 'vue'
const arrayInputs = ref(['1', 'test', '3'])
function change(i, event) {
arrayInputs.value[i] = event.target.value
}
</script>
<template>
<div class="vue-component">
<h2>ant-design-vue</h2>
<template v-for="(item, index) in arrayInputs" :key="index">
<!-- the index is important -->
<AInput :value="item" #change="event => change(index, event)" />
</template>
<div>{{ arrayInputs }}</div>
</div>
</template>

How can I reassign and repass props data with keeping its reactivity?

<template>
<p>
<input type="text" v-model="appInput">
{{ appInput }}
</p>
<ParentComponent :appInput='appInput' :appObject='appObject'/>
</template>
<script setup>
import ParentComponent from './components/ParentComponent.vue'
import { ref } from 'vue'
const appInput = ref('')
const appObject = ref({
text1: 'text1',
text2: 'text2'
})
</script>
<!-- ------------------------------------- -->
<template>
<!-- {{ appInput }} -->
{{ props.appInput }}
<!-- {{ appObject }} -->
{{ props.appObject.text1 }}
<ChildComponentVue :appInput='appInput'/>
</template>
<script setup>
import { defineProps } from 'vue';
import ChildComponentVue from './ChildComponent.vue';
const props = defineProps(['appInput', 'appObject'])
// const appInput = props.appInput
// const appObject = props.appObject
</script>
Using Vue 3, I want to reactively render text from App.vue on ParentComponent.vue and ChildComponent.vue by passing data from App to Parent, and Parent to child successively.
But when I reassign props data on ParentComponent for use it conveniently, it lose reactivity.
I tried repack it with ref() on ParentComponent, but as Vue3 Official Document says, it doesn't work.
But it is too dirty to use passed data without reassign in template (like {{ props.appObject.text1 }}) and too hard to repass not entire but just some part of passed data.
and here are my questions
Are there some ways to use and repass passed data more concisely without losing its reactivity?
What is the convension on Vue3 to deal with this kind of problem happen. Just using Vuex?

How to use template refs in Nuxt 3

In Nuxt2 there were template $refs that you could access in <script> with this.$refs
I would like to know what is the Nuxt3 equivalent of this is.
I need this to access the innerText of an element. I am not allowed to use querySelector or getElementById etc.
This is the way we write code. I can give html elements ref="fooBar" but I can't access it with this.$refs.fooBar or even this.$refs.
<script setup lang="ts">
import { ref, computed } from 'vue';
const foo = ref('bar');
function fooBar() {
//Do stuff
}
</script>
<template>
//Html here
</template>
With Options API
<script>
export default {
mounted() {
console.log('input', this.$refs['my-cool-div'])
}
}
</script>
<template>
<div ref="my-cool-div">
hello there
</div>
</template>
With Composition API
<script setup>
const myCoolDiv = ref(null)
const clickMe = () => console.log(myCoolDiv)
</script>
<template>
<button #click="clickMe">show me the ref</button>
<div ref="myCoolDiv">
hello there
</div>
</template>

Why won't vue.js update the DOM?

Working my way learning about Vue. I chose it as the better alternative after looking at React, Angular and Svelte.
I have a simple example that its not working probably because I'm not getting/understanding the reactive behaviour of Vue.
Plain simple App:
<template>
<div id="app">
<app-header></app-header>
<router-view />
<app-footer></app-footer>
</div>
</template>
<script>
import Header from './components/Header.vue'
import Home from './components/Home.vue'
import Footer from './components/Footer.vue'
export default {
components: {
name: 'App',
'app-header': Header,
'app-footer': Footer
}
}
</script>
Where Home.vue and Footer.vue have plain HTML content on the template.
On Header.vue I have:
<template>
<div>
<h1>The Header</h1>
<nav>
<ul>
<li>Curr Player: {{ ethaccount }}</li>
<li>Prop owner: {{ propOwner }}</li>
</ul>
</nav>
<hr />
</div>
</template>
<script>
export default {
data() {
return {
ethaccount: 'N/A',
propOwner: 'N/A'
}
},
methods: {
update() {
var ethaccount = '0xAAAAAA123456789123456789123456789'
console.log('ETH Account: ' + ethaccount)
var propOwner = '0xPPPPPPPPPPP987654321987654321'
console.log('Prop Account: ' + propOwner)
}
},
mounted() {
this.update()
}
}
</script>
But I'm unable to get the header updated and unable to find what I'm doing wrong. Help.
If you need to read a little bit more about the reactivity of the datas in vuejs check this link : https://v2.vuejs.org/v2/guide/reactivity.html
If you need to access/change your data try to do it like that :
this.$data.ethaccount = 'foo';
this.$data.propOwner = 'bar';
For me the problem is taht you re-declare your variable locally by doing :
var ethaccount = "0xAA...";
By doing such you never change the value of the data you're accessing through your template.
Hope it will solve your problem.

Async components in Vue2

I’d like to have all my routes to show Navbar and Footer except “Login” route - it should contain ONLY Logins component content.
In App.vue (my root component) I have this:
<template>
<router-view v-if="$route.name === 'Login'"></router-view>
<div v-else>
<app-nav></app-nav>
<div class="container">
<transition name="bounceLeft" mode="out-in" appear>
<router-view :key="$route.fullPath"></router-view>
</transition>
</div>
<app-footer></app-footer>
</div>
</template>
<script>
export default
{
components:
{
'AppNav': () => import( "#/components/AppNav.vue" ),
'AppFooter': () => import( "#/components/AppFooter.vue" )
}
}
</script>
<style>
</style>
It works, but as you can see, I want to “lazy load” my AppNav and AppFooter components, so they will be downloaded ONLY when they are needed (when routes name IS NOT ‘Login’). Unfortunately this doesnt work - when I go to Login route, these components are still downloaded from the server.
How can I achieve lazy-loading component in this example?
If you use webpack it will looks like this:
<script>
export default
{
components:
{
'AppNav': () => System.import( "#/components/AppNav.vue" ),
'AppFooter': () => System.import( "#/components/AppFooter.vue" )
}
} </script>
I don't know other way )