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>
Related
I have a component with a slot (SlotComponent) like this for example
<template>
<slot :element="element"></slot>
</template>
<script setup lang="ts">
import { onMounted, ref } from "vue";
const element = ref<HTMLElement | null>(null);
onMounted(() => console.log(element.value));
</script>
However I can't seems to bind the element within the slot when using the component. The element is null on the onMounted lifecycle callback (above snippet).
<SlotComponent v-slot="{ element }">
<div ref="element">hello world</div>
</SlotComponent>
Question: how can I bind to the html element inside the slot?
use Function Refs
provide a setElement() function as a slot prop
<template>
<slot :set-element="setElement"></slot>
</template>
<script setup lang="ts">
import { ref, watchEffect } from "vue";
const element = ref<Element | null>(null);
function setElement(el: Element) {
element.value = el;
}
watchEffect(() => {
console.log(element.value);
});
</script>
usage
<SlotComponent v-slot="{ setElement }">
<div :ref="(el) => setElement(el)">Hello World</div>
</SlotComponent>
When I have a child component like this:
<script setup>
import { defineExpose } from 'vue'
const validate = () => {
console.log('validate')
}
defineExpose({ validate })
</script>
<template>
hello
</template>
and parent component in which I use child:
<script setup>
import { ref } from 'vue'
const test = ref()
const validate = () => {
console.log('test', test.value)
}
</script>
<template>
<div ref="test">
<Child />
</div>
<button #click="validate">
click me
</button>
</template>
Is it possible to access validate method from the child component via template ref which is on the wrapper div in parent component?
EDIT:
I update my playground link in which I completed the task but I'm using parent instance instead of provide/inject:
https://sfc.vuejs.org/#eNqNU9FuozAQ/BWfXyBSYt4jqK463Un9hqOqKCypW7At29BWEf/eXcAkJVXaSEHszu4wnl0f+a0xou+A73nqSiuNZw58Z25yJVujrWdHZqFmA6utblmEpdEC/dO2nfMioYCYTvCdMp1f8DGaC0qtHCLUnhF9vMkVYyHvAR8Zizcsu2FHQqhS9EXTAT1lVXigliFXaTKpRr0YeGhNgyBFPh3lIXuWcyLIOaYZ/tJJWPKDMB2PNb6Gf/rYea8V+102snxBbpK7cI9J1sLUPJUilCaLNL7lkz+7tjDi2WmF3o+nzGfA5Xw/nZty6BjFOX/y3rh9kri6JBufndD2kOCbsJ3ysgUBrt09Wv3qwCJxzrdnHAkme7A7C6oCC/Ya56r0gpdo0fsBjxKmfm1/Kqilgr9vRjtYLVIY+TxVqdWttcU7Tv///QqDi5VgcQPnrUzXa6JN8PGUn3aN5NPnz7XFx+Vb2wtFA7ZdW7ZK9mGBXKNP+zPlP89/uQrXXDNW97JCJQfwfzqLw/B3aEehym9MXBlFmG5ANPoQR0uJJAm/oukSBQIZ+LMvPjr6hvpqFoc6YQqqEP7dgHh4UEWLrVnGItqKaPZ+XQyj11W4yMFgYTr3FAd931/un/s9fAD8ILMq
How to actually get rid of parent instance and use provide inject to achieve same result as in the playground from link above?
The ref needs to be on the actual Child element, not the parent div. The method is a property of test.value, so if the method is called "validate" you can run it with test.value.validate().
You also need to make sure the Child component is imported
Try this SFC Playground instead. The "click me" button will console.log the word "validate" which comes from the Child component.
<script setup>
import Child from './Child.vue'
import { ref } from 'vue'
const test = ref()
const childFunc = () => {
test.value.validate()
}
</script>
<template>
<div>
<Child ref="test" />
</div>
<button #click="childFunc">
click me
</button>
</template>
Goal
How to implement a component that renders an html string (eg fetched from a CMS) passed as a slot like this :
// app.vue
<script setup>
import MyComponent from "./MyComponent.vue"
const htmlStr = `not bold <b>bold</b>`
</script>
<template>
<MyComponent>{{htmlStr}}</MyComponent>
</template>
Explanation
To render an html string (eg fetch from a CMS) we can use v-html :
// app.vue
<script setup>
const htmlStr = `not bold <b>bold</b>`
</script>
<template>
<p v-html="htmlStr"></p>
</template>
Failed attempts
I have tried with no success :
// component.vue
<script>
import { h } from "vue";
export default {
setup(props, { slots }) {
return () =>
h("p", {
innerHTML: slots.default(),
});
},
};
</script>
Renders
[object Object]
Link to playground
Workaround with props
As a workaround, we can of course use props but it's verbose.
// app.vue
<template>
<MyComponent :value="htmlStr">{{htmlStr}}</MyComponent>
</template>
// component.vue
<template>
<p v-html="value"></p>
</template>
<script setup>
import { defineProps } from 'vue'
defineProps(['value'])
</script>
slots.default() returns an array of your passed slot elements, try to map that content and render it :
h("p", {
innerHTML: slots.default().map(el=>el.children).join(''),
});
Playground
The documentation is not enough to be able to do the emit. I have seen many tutorials and nothing works, now I am testing this
Child component
<div #click="$emit('sendjob', Job )"></div>
With the Vue DevTools plugin I can see that the data is sent in the PayLoad, but I can't find a way to receive this emit from the other component.
Many people do this
Any other component
<template>
<div #sendjob="doSomething"></div>
</template>
<script>
export default {
methods:{
doSomething(){
console.log('It works')
}
}
}
</script>
In my case it doesn't work
You should import the child component in the parent component and use it instead of the regular div tag.
I'm sharing examples for your reference to achieve emits in Vue 3 using <script setup> and Composition API. I strongly suggest going with <script setup if you are going to use Composition API in Single File Component. However, the choice is yours.
Example with <script setup>: https://v3.vuejs.org/api/sfc-script-setup.html
<!-- App.vue -->
<template>
<UserDetail #user-detail-submitted="userDetailSubmitted"/>
</template>
<script setup>
import UserDetail from './components/UserDetail';
function userDetailSubmitted(name) {
console.log({ name })
}
</script>
<!-- UserDetail.vue -->
<template>
<input type="text" v-model="name" #keyup.enter="$emit('user-detail-submitted', name)" />
</template>
<script setup>
import { ref } from 'vue';
const name = ref('');
</script>
Example using Composition API: https://v3.vuejs.org/api/composition-api.html
<!-- App.vue -->
<template>
<UserDetail #user-detail-submitted="userDetailSubmitted"/>
</template>
<script>
import UserDetail from "./components/UserDetail";
export default {
components: {
UserDetail,
},
setup() {
function userDetailSubmitted(name) {
console.log({ name });
}
return {
userDetailSubmitted
}
},
};
</script>
<!-- UserDetail.vue -->
<template>
<input type="text" v-model="name" #keyup.enter="$emit('user-detail-submitted', name)" />
</template>
<script>
import { ref } from 'vue';
export default {
setup() {
const name = ref('');
return {
name,
}
}
}
</script>
You should import this child-component in the parent. And don't rename it to the html's original tag.vue3. You'd better use the Composition API.
I have an app that shares an external module with other applications by sharing a global javascript object.
One of these apps is developed with vue 2 and when the global object is updated in the external module, the option data property of vue 2 is updated perfectly while in vue 3 it is not. I also tried with the new reactive property but nothing to do, is it a bug?
Not being able to make any changes to the external module because it is shared with other apps, how can I make it work in vue 3?
Here are some test links:
Vue 2 share external object
<script src="https://unpkg.com/vue"></script>
<script>
var EXTERNAL_OBJECT={
name:"Bob",
list:[{name:"Ivan"}]
}
function change_object(){
EXTERNAL_OBJECT.name+="+++"
EXTERNAL_OBJECT.list.push({name:"Carl"})
}
</script>
<button onClick="change_object()">change external object</button>
<div id="app">
<div>
{{share.name}}
</div>
<div v-for="item in share.list">
{{item.name}}
</div>
</div>
<script>
new Vue({
el: '#app',
data: {
share:EXTERNAL_OBJECT
}
})
</script>
Vue 3 share external object
<script src="https://unpkg.com/vue#3.2.4/dist/vue.global.js"></script>
<script>
var EXTERNAL_OBJECT={
name:"Bob",
list:[{name:"Ivan"}]
}
function change_object(){
EXTERNAL_OBJECT.name+="+++"
EXTERNAL_OBJECT.list.push({name:"Carl"})
}
</script>
<button onClick="change_object()">change external object</button>
<div id="app">
<div>
{{share.name}}
</div>
<div v-for="item in share.list">
{{item.name}}
</div>
</div>
<script>
const app = Vue.createApp({
data () {
return {
share:EXTERNAL_OBJECT
}
}
});
app.mount('#app')
</script>
Vue 3 share external object with reactive property
<script src="https://unpkg.com/vue#3.2.4/dist/vue.global.js"></script>
<script>
var EXTERNAL_OBJECT={
name:"Bob",
list:[{name:"Ivan"}]
}
function change_object(){
EXTERNAL_OBJECT.name+="+++"
EXTERNAL_OBJECT.list.push({name:"Carl"})
}
</script>
<button onClick="change_object()">change external object</button>
<div id="app">
<div>
{{share.name}}
</div>
<div v-for="item in share.list">
{{item.name}}
</div>
</div>
<script>
const { createApp, reactive } = Vue
const app = createApp({
setup(){
let share = reactive(EXTERNAL_OBJECT)
return {
share
}
},
data () {
return {
msg:"reactive test"
}
}
});
app.mount('#app')
</script>
thanks
It is a bit hard to read ... I just look at the Vue3 Example.
How many file are you showing?
Cant write your EXTERNAL_OBJECT directly in the reactive property? Like:
const EXTERNAL_OBJECT = reactive({ name:"Bob",
list:[{name:"Ivan"}] });