How to access name in <script> from <script setup> in Vue3? - vue.js

I want to access the "name" variable from <script> in my <script setup> block. I cant seem to figure out how to do it. I have tried importing options from '*.vue' but that prompts me to install module '*.vue'.
<script>
export default {
name: 'some name'
}
</script>
<script setup>
//use the 'name' variable here
</script>

Unfortunately, this thing can't be done. You could use ref and access its value anywhere in the file.
<template>
<h1>{{ name }}</h1>
</template>
<script setup>
import {ref} from 'vue';
const name = ref('Jorgen');
</script>
If you want to access it's value inside <script setup> you have to use .value.I have provided the following example in case you want to use it inside a method within <script setup>,
<script setup>
import {ref} from 'vue';
const name = ref('Jorgen');
const showMyName = () => {
alert(name.value);
}
</script>
In brief, if you want to use name inside the template don't use .value, if you want to use name inside use .value

You can access options exported from <script> by creating a circular dependency to the Vue component:
<script>
export default { name: "MyComponent" }
</script>
<script setup>
// HACK: Create a circular dependency to this file to get access to options.
import ThisComponent from "./MyComponent.vue"
console.log(`Setting up ${ThisComponent.name}`);
</script>
It also works with TypeScript (lang="ts").

Related

#vuepic/vue-datepicker does not work with vite build [duplicate]

I have the following View in Vue:
<script setup>
import Overwrite from "../components/Overwrite.vue";
</script>
<template>
<div>
...
<textarea v-model="text" cols="99" rows="20"></textarea>
...
</div>
</template>
<script>
export default {
data() {
return {
text: ""
};
},
components: { Overwrite: Overwrite },
};
</script>
Everything works perfectly fine when I start the application with npm run dev.
However, when I build the app for production and run it, I get the following error as soon as I type anything into the textarea:
index.57b77955.js:3 Uncaught ReferenceError: text is not defined
at HTMLTextAreaElement.t.onUpdate:modelValue.s.<computed>.s.<computed> [as _assign] (index.57b77955.js:3:1772)
at HTMLTextAreaElement.<anonymous> (vendor.31761432.js:1:53163)
I also have other form elements that show the exact same behaviour.
You can use a maximum of 1 × <script> tag and a maximum of 1 × <script setup> per vue component.
Their outputs will be merged and the object resulting from merging their implicit or explicit exports is available in <template>.
But they are not connected. Which means: do not expect any of the two script tags to have visibility over the other one's imports.
The worst part is that, although the first <script setup> does declare Ovewrite when you import it (so it should become usable in <template>), the second one overwrites it when you use components: { Overwrite: Overwrite }, because Overwrite is not defined in the second script. So your components declaration is equivalent to:
components: { Overwrite: undefined }
, which overwrites the value already declared by <script setup>.
This gives you two possible solutions:
Solution A:
<script>
import Overwrite from "../components/Overwrite.vue";
export default {
components: {
Overwrite
},
// you don't need `data` (which is Options API). use `setup` instead
setup: () => ({
text: ref('')
})
}
</script>
Solution B:
<script setup>
import Overwrite from "../components/Overwrite.vue";
const text = ref('')
</script>
Or even:
<script setup>
import Overwrite from "../components/Overwrite.vue";
</script>
<script>
export default {
data: () => ({ text: "" })
};
</script>
Can you try using only the setup script tag? Using it only for imports this way doesn't make sense. If you import a component in setup script tags you don't need to set components maybe the issue is related to that.
Also you could try the full setup way then:
<script setup>
import { ref } from 'vue'
import Overwrite from "../components/Overwrite.vue";
const text = ref('')
</script>
<template>
<div>
...
<textarea v-model="text" cols="99" rows="20"></textarea>
...
</div>
</template>

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>

Vue 3: component `:is` in for loop fails

I'm trying to loop over a list of component described by strings (I get the name of the component from another , like const componentTreeName = ["CompA", "CompA"].
My code is a simple as:
<script setup>
import CompA from './CompA.vue'
import { ref } from 'vue'
// I do NOT want to use [CompA, CompA] because my inputs are strings
const componentTreeName = ["CompA", "CompA"]
</script>
<template>
<h1>Demo</h1>
<template v-for="compName in componentTreeName">
<component :is="compName"></component>
</template>
</template>
Demo here
EDIT
I tried this with not much success.
Use resolveComponent() on the component name to look up the global component by name:
<script setup>
import { resolveComponent, markRaw } from 'vue'
const myGlobalComp = markRaw(resolveComponent('my-global-component'))
</script>
<template>
<component :is="myGlobalComp" />
<template>
demo 1
If you have a mix of locally and globally registered components, you can use a lookup for local components, and fall back to resolveComponent() for globals:
<script setup>
import LocalComponentA from '#/components/LocalComponentA.vue'
import LocalComponentB from '#/components/LocalComponentB.vue'
import { resolveComponent, markRaw } from 'vue'
const localComponents = {
LocalComponentA,
LocalComponentB,
}
const lookupComponent = name => {
const c = localComponents[name] ?? resolveComponent(name)
return markRaw(c)
}
const componentList = [
'GlobalComponentA',
'GlobalComponentB',
'LocalComponentA',
'LocalComponentB',
].map(lookupComponent)
</script>
<template>
<component :is="c" v-for="c in componentList" />
</template>
demo 2
Note: markRaw is used on the component definition because no reactivity is needed on it.
When using script setup, you need to reference the component and not the name or key.
To get it to work, I would use an object where the string can be used as a key to target the component from an object like this:
<script setup>
import CompA from './CompA.vue'
import { ref } from 'vue'
const components = {CompA};
// I do NOT want to use [CompA, CompA] because my inputs are strings
const componentTreeName = ["CompA", "CompA"]
</script>
<template>
<h1>Demo</h1>
<template v-for="compName in componentTreeName">
<component :is="components[compName]"></component>
</template>
</template>
To use a global component, you could assign components by pulling them from the app context. But this would require the app context to be available and the keys known.
example:
import { app } from '../MyApp.js'
const components = {
CompA: app.component('CompA')
}
I haven't tested this, but this might be worth a try to check with getCurrentInstance
import { ref,getCurrentInstance } from 'vue'
const components = getCurrentInstance().appContext.components;

How to pass createApp in Vue 3 among the app?

I'm migrating from Vue 2 to Vue 3. In Vue 2, it's possible to register a component globally like this: main.js
new Vue({
el: '#app'
})
A global registration of a component: my-component.js
Vue.component('my-component', {
template: '<div>Hi!</div>'
})
I'm using Webpack for import Single File Components in other components, and then i'm bundling it in one file. With Vue 2, that was easy because Vue instance was globally registered. Now with Vue 3, that doesn't work any more because of Vue.createApp({}).mount('#app'). I was trying to do something like this: main.js
const app = createApp({});
export const app;
And in: my-component.js
import { app } from './main.js';
app.component('my-component', {
template: '<div>Hi!</div>'
})
And at the end: close-app.js
import { app } from './main.js';
app.mount('#app');
And in: index.html
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/3.0.5/vue.global.js"></script>
<script type="application/javascript" src="my-component.js"></script>
<div id="app">
<my-component></my-component>
</div>
<script type="application/javascript" src="close-app.js"></script>
I'm importing the app because of Webpack. Webpack is bundling it in self-invoking functions, but that doesn't work. I know there is a way to declare it globally like this: window.app = app;, but that's not a good idea. Can someone help me with that?
Components Webpacks
Since Vue 3 component registration has to be invoked on an app instance, and you're importing each component Webpack individually, I don't think you can avoid globals. Using a global app instance (window.app) in your components might be the easiest solution, especially if you want to minimize the changes.
Alternatively, you could build the components as UMD modules, where the component definition is exported as a global, import the UMD module with the <script> tag (as you're already doing), and call app.component() for each of them:
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/3.0.5/vue.global.js"></script>
<!-- sets MyComponent global -->
<script type="application/javascript" src="./my-component.umd.js"></script>
<div id="app">
<my-component></my-component>
</div>
<script>
const app = Vue.createApp({})
app.component(MyComponent.name, MyComponent)
app.mount('#app')
</script>
Component Module Scripts
In main.js, use createApp from the global Vue that you've imported in the <script> tag:
export const app = Vue.createApp({})
Then import your scripts as modules with <script type="module">:
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/3.0.5/vue.global.js"></script>
<script type="module" src="./my-component.js"></script>
<div id="app">
<my-component></my-component>
</div>
<script type="module" src="./close-app.js"></script>
demo

Vue Component. Require is not defined

Using Vue.js I have this in my /Component/App.vue
import Vue from 'vue';
import VueFusionCharts from 'vue-fusioncharts';
import FusionCharts from 'fusioncharts';
import Column2D from 'fusioncharts/fusioncharts.charts';
import FusionTheme from 'fusioncharts/themes/fusioncharts.theme.fusion';
Vue.use(VueFusionCharts, FusionCharts, Column2D, FusionTheme);
export default {
name: 'app',
data () {
return {
}
}
<template>
<div id="appp">
<div id="chart-container">
</div>
</div>
</template>
In my js/examplevue.js Script I have
Vue.component('Chart', require('./components/App.vue'));
var app = new Vue({
el: '#app',
});
Then in my balde i have :
<div class=" " id="app">
<Chart>.</Chart>
</div>
<script src="{{ asset('js/examplevue.js') }}"></script>
I catch the error : Require is not defined. in Exemple.js
Usually, my vuejs code is working until i try to integer a component.
It looks like your App.vue file is malformed and there are two other errors:
in your template, the div id is mispelled as "appp" instead of "app" as defined in your examplevue.js file
I also noticed you were missing a closing } on your export default statement
If you want to use a <template> tag section you must also enclose all of your Javascript in <script> tags (see code below). :
/Component/App.vue
<template>
<div id="app">
<div id="chart-container">
</div>
</div>
</template>
<script>
import Vue from 'vue';
import VueFusionCharts from 'vue-fusioncharts';
import FusionCharts from 'fusioncharts';
import Column2D from 'fusioncharts/fusioncharts.charts';
import FusionTheme from 'fusioncharts/themes/fusioncharts.theme.fusion';
Vue.use(VueFusionCharts, FusionCharts, Column2D, FusionTheme);
export default {
name: 'app',
data () {
return {}
}
}
</script>
you may also have to put the following code in your examplevue.js file
import Vue from 'vue';
in order to create a new Vue instance.
Hope that helps!