I am exporting a value from a JS file and using it in my Vue. I display the value like in my template, but I would like to also use this value elsewhere, outside of the template.
Here is a simplified version of my setup and what I would like to achieve:
In my myJS.js (in actual app this file is used to call my API):
const appService = {
getPosts() {
return 123456;
}
}
export default appService
and then I have a VUE component that looks like this:
<template>
{{ info }}
</template>
alert(info);
import appService from '../myJS'
export default {
name: 'myvue',
props: {
msg: String
},
data () {
return {
info: {}
}
}
async created () {
this.info = await appService.getPosts();
}
}
The value is displayed in the template, but the alert(info) triggers a 'info is undefined' error.
How can I use this value in normal JavaScript outside of my template?
Hope that makes sense.
Thanks in advance!
There's basically two ways:
Assign the value to a variable, inside the file, but outside the component definition. So in your example you would say let info; and in your created() hook you would go this.info = info = await appService.getPosts().
Assign the component itself to a variable before you export it:
const myComponent = { component definition... }
// Access with myComponent.info
export default myComponent;
However! You will see that myComponent.info is initially undefined. And unlike in the template, the value of info will not reactively update when the asynchronous call resolves. Reactivity magic only works in the template and in the code of the actual component, e.g. in computed and watch properties. How to handle the specifics of asynchronous loading elegantly really depends on the specifics of how you want to use this variable.
Finally: the reason you don't just get access to the variable throughout the .vue file is because under the hood, the template compiles to a function which is executed in the context of the component. For convenience, all of the fields of the component are then made available as variables without the usual this prefix. In fact, in your template, info is treated as a shorthand for this.info. (Notice that you can substitute this.info for info in the template and it will still work.) This privilege doesn't apply to any other code in the file; that stuff is just vanilla js code.
Related
The situation
In my Vue app, I have a Vue component which mounts an svg, which I have defined with a few props. These are a combination of reactive and non-reactive data.
The reactive data that we need is percData, which therefore sits in the data(){} object.
We also have colours, width, height, and scale, which are not reactive and never will be. I don't call these in the <template> block, and I don't plan for them to change. These are currently declared with const, and are not within the export defautl{} block scope.
The question(s)
Where is the best place to be for these const declarations?
What scope are these const-declared variables currently in?
More generally, how does scope work for the <script> tag in Vue in a multi-component app? Is each script/component a separate scope from the global one? And where is the global scope?
My possible understanding
As per this thread and this thread, I think the best place for my const would be in a separate file, from which I would then import them in my mySvgComponent component. Is this the correct approach?
My code
<template>
<div></div>
</template>
<script>
import { mySvgComponent} from '../mySvgComponent'
import { select } from 'd3'
const [colour1, colour2, colour3, colour4] = ['#000000', '#111111', '#222222', '#3333'];
const width = 135
const height = 135
const scale = .75
export default {
name:'mySvgComponent',
data(){
return{
percData: null
}
},
props: {
summary: Object
},
methods: {
percSetup(summary) {
return this.percData = [
{ colour: colour1, perc: +summary.colour1Percentage.toFixed(2)},
{ colour: colour2, perc: +summary.colour2Percentage.toFixed(2)},
{ colour: colour3, perc: +summary.colour3Percentage.toFixed(2)},
{ colour: colour4, perc: +summary.colour4Percentage.toFixed(2)}
]
}
},
async mounted() {
this.percSetup(this.$props.summary)
const svg =
select('div')
.append('svg')
.call(mySvgComponent(this.percData)
.width(width)
.height(height)
.scale(scale))
}
}
</script>
<style></style>
Related threads and why I don't think they answer my question:
How to set a component non-reactive data in Vue 2?, How to make a template variable non-reactive in Vue, How could I use const in vue template?. I don't call my const variables in my <template> tag, and I don't need it to be responsive.
What is the best way to create a constant, that can be accessible from entire application in VueJs ?. Maybe I don't understand this fully. Why would I need to run a method() to return my const variables?
Vue SFC is syntax sugar, it's necessary to understand what code it's compiled to in order to use it effectively.
The result of SFC script syntax is ES module with roughly the same content as the body of <script> block, with name and render options being added by the compiler. Then general modular JavaScript practices are applicable. The constants can remain in current module if they don't take much lines and aren't reused in other modules, and can be moved to a separate module otherwise. In case the constants are supposed to be used in a template, they can be returned from data or setup function.
The result of SFC script setup syntax is ES module with the whole block being moved to generated setup function, with the exception of imports. In this case it's inefficient to declare constants in this block because they will be created on each component instantiation, this may be a reason to move them to a separate module, although this can be considered preliminary optimization.
Considering the above, the scope of Vue SFC modules works exactly like it's expected from ESM because this is what they are compiled to.
Vue composition API provides markRaw function to additionally prevent constant objects from being made reactive when they are used inside reactive ones like data, in case this is unwanted. The same is done for options API with Object.freeze in linked question.
TL;DR: the code in the question is ok, it's correct to use the constants like that in this case.
In Vue.js, you can set non-reactive (i.e., constant) variables by declaring them in the data object as a function that returns an object, rather than declaring them directly as properties of the data object. This ensures that the variables are only set once during the initialisation of the component and cannot be modified later. Here's an example:
data: function () {
return {
nonReactiveConst: 'This is a non-reactive const variable'
}
}
Another way to create a non-reactive variable in Vue is to use the Vue.observable() function to create an observable object, and then assign the non-reactive variable as a property of that object.
const state = Vue.observable({
nonReactiveConst: 'This is a non-reactive const variable'
});
You can then access the non-reactive variable inside the component's template using state.nonReactiveConst.
It's important to note that using this method, you can still mutate properties of the object, but you can't reassign the whole object.
const app = createApp({
data() {
return {
some_id: 0
}
}
})
I have an autocomplete on a field.
When a label is selected, I want to pass the id to a Vue app.
onSelectItem: ({label, value}) => {
app.some_id = value;
}
This worked in an old v2 version of Vue.js.
Now, I can't even call the methods of the Vue app from other JavaScript functions.
What is the best solution?
There are certain circumstances where you may need to access and mutate, change the instance's data.
This was easier in Vue JS 2, but Vue JS 3 has become more encapsulated. However it does not mean mutating state from outside is impossible. You can read about it here
Supposing that you are using Vue with build steps, which covers most cases, you will have something like this:
const app = createApp({
data() {
return {}
},
})
.mount('#app');
Now if you head to browser console and type app, it will be null because it is limited to the scope of compiled .js files.
But if you attach app to a global object, document for example:
document.app = createApp({
data() {
return {}
},
})
.mount('#app');
Now if you type app in the console, it will no longer be null, but the Vue instance. From there, you can access the instance's data as well as mutate it via app.$data property.
Now what if the instance has components and you want to mutate their $data? In previous versions, it was possible to access children via $children property. But now in order to access children, you have to give each of them a ref, then access via their ref name. For example:
app.$refs.alertComponent.$data.message = "New message!"
I found a seloution for how to access to vue-router and other services that works only inside the vue component. In the created option of App.vue i save the "this" of vue app inside state of vuex.
does it's a good way or can make some problems in future?
instead to write:
this.$router.push('route-name')
from some component, is save the this keyword in the state as context key for example,
and now from the Actions i'm using the context instead this
such like this:
state.context.router.push('route-name')
and its works..
my question is if its good solution or not.
I think that is not a good solution, if you want to use this, you should try with the actions or getter returned value, inside a Vue file instead using it directly in an action.
I'm giving an example and an explanation
Example:
Getter:
export function getRedirect(state){
return state.routerName
}
Vue file:
<template>
Page
<button #onClick="redirect" > Redirect </button>
</template>
<script>
import {mapGetters} from 'vuex'
export default: {
computed:{
...mapGetters('VuexStoreName',['getRedirect'])
},
methods:{
redirect(){
this.$router.push(this.getRedirect)
}
}
}
</script>
So, instead of using the redirect inside an JS file, creating an action and passing this inside the parameter or getting it by state, you'll be using it correctly in a Vue file.
The probblem setting this in the state is that when it changes the instance saved will not change, and you'll have to update every time and everywhere. And calling this inside a Vue file, it'll always be updated
I have some code similar to below where i am importing a component dynamically and then i want to set some props on it.
I dont have a <component></component> html tag as i am passing the component to a 3rd party plugin that will display the component inside their own component.
the below code works but then i have started seeing the following vue warning error in the console:
option "propsData" can only be used during instance creation with the `new` keyword.
structure of settings variable:
const settings = {
props:{
title: "new title"
}
}
component loading function:
function Load(compName, settings){
import("#/"+ compName +".vue").then((mod)=>{
let mycomponent = mod.default;
const props = settings.props;
mycomponent.propsData = {...props});
//3rd party package needs this format
let template = function(){
return {template : mycomponent};
}
//not shown - code that passes the template variable to the 3rd party package
});
}
is there another way to get the props into the component - or is it ok to ignore this warning?
I'm using the created lifecycle hook in vue.js to load data from my store to the data for a vue component. I noticed that this.selectedType = store.state.selectedType successfully loads the data from the store. However, if I use the getter to load from the store (i.e. this.selectedType = store.getters.getType()), I get the following error:
Error in created hook: "TypeError: Cannot read property 'selectedType' of undefined"
I don't understand why it is saying that selectedType is undefined because selectedType has the value "Item" in the store and is correctly loaded on create if I use this.selectedType = store.state.selectedType.
The getter is defined as such:
getters: {
getSelectedType: state => {
return state.selectedType
}
}
And the state is defined as:
state: {
selectedType: "Item"
}
Could someone please explain why this occurs? I'm hunch is that there is something about the lifecycles that I don't fully understand that is leading to this confusion.
You are not supposed to call getters. Just like computed properties, you instead write it like you are reading a variable. In the background the function you defined in the Vuex store is called with the state, getters (and possibly rootState and rootGetters) and returns some value.
Beside that, it is usually an anti-pattern to use a lifecycle hook to initialise any variable. Local 'component' variables can be initialised in the data property of the component, while things like the vuex state usually end up in a computed property.
The last thing I want to point out is that, if you have correctly added the store to your Vue application, you can access the store in any component with this.$store. To use getters in your application, you can use the mapGetters helper to map getters to component properties. I would recommend using something like this:
import { mapGetters } from 'vuex';
export default {
// Omitted some things here
computed: {
...mapGetters({
selectedType: 'getSelectedType'
})
},
methods: {
doSomething () {
console.log(this.selectedType);
}
}
}
Which is functionally equivalent to:
computed: {
selectedType () {
return this.$store.getters.getSelectedType;
}
}