Problem to add a root property to a vue app - vue.js

Could someone tell me what is wrong with this setup, where I want to load a config.json file before the vue app is created and access the config in the components with this.$root.config. There is no root config element I can access? Missing something? Thanks for your help! The config.json file is correctly loaded, can log the config to the console. But it is not added to the root properties from Vue?
fetch('/config.json')
.then(res => res.json())
.then(config => {
createApp(App, {
data() {
return config
},
created() {
console.log(this.$root.config);
}
}).use(store).use(router).use(i18n).mount('#app');
});

What you place in data won't be found in $root but, as Abdelillah pointed out, in $root.$data. Since App is the root component, though, you can just use this.config. In any subcomponent, you'd have to use this.$root.$data.config.
But Vue 3 provides a cleaner alternative to provide data to any component in your app: config.globalProperties.
Example:
const app = Vue.createApp({});
app.component('test', {
mounted() {
console.log(this.config);
}
});
Promise.resolve({
foo: 'bar'
}).then(config => {
app.config.globalProperties.config = config;
app.mount('#app');
});
<script src="https://unpkg.com/vue#next/dist/vue.global.prod.js"></script>
<div id="app">
<test />
</div>
As you can see, logging <test>'s .config outputs the globalProperty.config set on the app, and it's going to be the same for any component in the app.
If you want to provide data to any descendants of current component (no matter how deep, skipping intermediary parents), you could use provide/inject. While I find this particularly useful for providing some data to all the children of a particular component (and not to the rest of the app's components), it can obviously be used on the root component, which would make the provide available cross-app via inject wherever you need it.

there is no property called config on your data, what you are doing is simply returning the JSON object you imported, you should be doing:
fetch('/config.json')
.then(res => res.json())
.then(config => {
createApp(App, {
data() {
return {
// config: config
config
}
},
created() {
console.log(this.$root.$data.config);
// or console.log(this.config);
}
}).use(store).use(router).use(i18n).mount('#app');
});

Related

How to create dynamic components in vueJS

I am new to vueJS and am trying to load components dynamically. I searched on the web and experimented with many suggestions but am still not able to get it to work.
Scenario: I want to have a 'shell' component that acts as a container for swapping in and out other components based on user's selection. The file names of these components will be served up from a database
Here's the shell component:
<template>
<keep-alive>
<component :is='compName'></component>
</keep-alive>
</template>
<script>
props: ['vueFile'],
export default ({
data() {
compName: ()=> import('DefaultPage.vue')
},
watch: {
compName() {
return ()=> import(this.vueFile);
}
}
})
</script>
This does not work.
If I hard code the component file name, it works correctly... e.g.
return ()=> import('MyComponent.vue');
The moment I change the import statement to use a variable, it gives an error even though the variable contains the same string I hard code.
What am I doing wrong?
compName() {
const MyComponent = () => import("~/components/MyComponent.js");
}
You can see this post
https://vuedose.tips/dynamic-imports-in-vue-js-for-better-performance
You can put the components you want to add dynamically in a directory, for example: ./component, and try this
compName () {
return ()=> import(`./component/${this.vueFile}`);
}
The import() must contain at least some information about where the module is located.
https://webpack.js.org/api/module-methods/#dynamic-expressions-in-import

Vue/Nuxt: How to define a global method accessible to all components?

I just want to be able to call
{{ globalThing(0) }}
in templates, without needing to define globalThing in each .vue file.
I've tried all manner of plugin configurations (or mixins? not sure if Nuxt uses that terminology.), all to no avail. It seems no matter what I do, globalThing and this.globalThing remain undefined.
In some cases, I can even debug in Chrome and see this this.globalThing is indeed defined... but the code crashes anyway, which I find very hard to explain.
Here is one of my many attempts, this time using a plugin:
nuxt.config.js:
plugins: [
{
src: '~/plugins/global.js',
mode: 'client'
},
],
global.js:
import Vue from 'vue';
Vue.prototype.globalFunction = arg => {
console.log('arg', arg);
return arg;
};
and in the template in the .vue file:
<div>gloabal test {{globalFunction('toto')}}</div>
and... the result:
TypeError
_vm.globalFunction is not a function
Here's a different idea, using Vuex store.
store/index.js:
export const actions = {
globalThing(p) {
return p + ' test';
}
};
.vue file template:
test result: {{test('fafa')}}
.vue file script:
import { mapActions } from 'vuex';
export default {
methods: {
...mapActions({
test: 'globalThing'
}),
}
};
aaaaaaaaand the result is.........
test result: [object Promise]
OK, so at least the method exists this time. I would much prefer not to be forced to do this "import mapActions" dance etc. in each component... but if that's really the only way, whatever.
However, all I get is a Promise, since this call is async. When it completes, the promise does indeed contain the returned value, but that is of no use here, since I need it to be returned from the method.
EDIT
On the client, "this" is undefined, except that..... it isn't! That is to say,
console.log('this', this);
says "undefined", but Chrome's debugger claims that, right after this console log, "this" is exactly what it is supposed to be (the component instance), and so is this.$store!
I'm adding a screenshot here as proof, since I don't even believe my own eyes.
https://nuxtjs.org/guide/plugins/
Nuxt explain this in Inject in $root & context section.
you must inject your global methods to Vue instance and context.
for example we have a hello.js file.
in plugins/hello.js:
export default (context, inject) => {
const hello = (msg) => console.log(`Hello ${msg}!`)
// Inject $hello(msg) in Vue, context and store.
inject('hello', hello)
// For Nuxt <= 2.12, also add 👇
context.$hello = hello
}
and then add this file in nuxt.config.js:
export default {
plugins: ['~/plugins/hello.js']
}
Use Nuxt's inject to get the method available everywhere
export default ({ app }, inject) => {
inject('myInjectedFunction', (string) => console.log('That was easy!', string))
}
Make sure you access that function as $myInjectedFunction (note $)
Make sure you added it in nuxt.config.js plugins section
If all else fails, wrap the function in an object and inject object so you'd have something like $myWrapper.myFunction() in your templates - we use objects injected from plugins all over the place and it works (e.g. in v-if in template, so pretty sure it would work from {{ }} too).
for example, our analytics.js plugin looks more less:
import Vue from 'vue';
const analytics = {
setAnalyticsUsersData(store) {...}
...
}
//this is to help Webstorm with autocomplete
Vue.prototype.$analytics = analytics;
export default ({app}, inject) => {
inject('analytics', analytics);
}
Which is then called as $analytics.setAnalyticsUsersData(...)
P.S. Just noticed something. You have your plugin in client mode. If you're running in universal, you have to make sure that this plugin (and the function) is not used anywhere during SSR. If it's in template, it's likely it actually is used during SSR and thus is undefined. Change your plugin to run in both modes as well.
This would be the approach with Vuex and Nuxt:
// store/index.js
export const state = () => ({
globalThing: ''
})
export const mutations = {
setGlobalThing (state, value) {
state.globalThing = value
}
}
// .vue file script
export default {
created() {
this.$store.commit('setGlobalThing', 'hello')
},
};
// .vue file template
{{ this.$store.state.globalThing }}

Nuxt/Vuejs - How to create utils that have access to modules?

I am using asiox/vuejs to create a webpage. However I want to compartmentalize the code more. One example is I use axios to make requests to the backend, and the data in the response is commited into vuex.
this.$axios.get('events').then((response) => {
this.$store.commit('data/populate', response.data)
})
.catch((e) => {
console.error(e)
})
I want to write a util method for this, like this.$populate.events()
I have tried creating utils inside the plugins/ directory, but they dont have access to this.$axios or this.$store
Note that I have axios and vuex imported in nuxt.config.js
How can this be achieved?
If you need the function in the context, Vue instances and maybe even
in the Vuex store, you can use the inject function, which is the
second parameter of the plugins exported function.
Injecting content into Vue instances works similar to when doing this
in standard Vue apps. The $ will be prepended automatically to the
function.
Reference
export default ({ app, store }, inject) => {
inject("populate", () => {
app.$axios
.get("events")
.then(response => {
store.commit("data/populate", response.data);
})
.catch(e => {
console.error(e);
});
});
};
app variable is context property.
The root Vue instance options that includes all your plugins. For
example, when using axios, you can get access to $axios through
context.app.$axios.
Figured it out not 5 minutes after posting ...
Basically use this nuxt guide
And replace this with app in the method you'd like to move

Page reload causes Vuex getter to return undefined

Using Vue.js (Vuetify for FE).
A page reload causes the getter in Vuex to fail with pulling required data from the store. The getter returns undefined. The code can be found on GitHub at: https://github.com/tineich/timmyskittys/tree/master/src
Please see the full details on this issue at timmyskittys.netlify.com/stage1. This page has complete info on the issue and instructions on how to view the issue.
Note, there is mention of www.timmyskittys.com in the issue description. This is the main site. timmyskittys.netlify.com is my test site. So, they are the same for all intents and purposes. But, my demo of this issue is at the Netlify site.
I read the complete issue in the website you mentioned. It's a generic case.
Say, for cat details page url: www.timmyskittys.com/stage2/:id.
Now in Per-Route Guard beforeEnter() you can set the cat-id in store. Then from your component call the api using the cat-id (read from getters)
I found the solution to my issue:
I had to move the call of the action which calls the mutation that loads the .json file (dbdata.json) into a computed() within App.vue. This was originally done in Stage1.vue.
Thanks all for responding.
I had the same issue and my "fix" if it can be called that was to make a timer, so to give the store time to get things right, like so:
<v-treeview
:items="items"
:load-children="setChildren"
/>
</template>
<script>
import { mapGetters } from 'vuex'
const pause = ms => new Promise(resolve => setTimeout(resolve, ms))
export default {
data () {
return {
children: []
}
},
computed: {
...mapGetters('app', ['services']),
items () {
return [{
id: 0,
name: 'Services',
children: this.children
}]
}
},
methods: {
async setChildren () {
await pause(1000)
this.children.push(...this.services)
}
}
}
</script>
Even though this is far from ideal, it works.

How to watch on Route changes with Nuxt and asyncData

Hi everybody i'm trying to watch on route changes in my nuxt js app.
Here my middleware:
export default function ({ route }) {
return route; but i don't know what to write here
}
index.vue File
middleware: [routeReact]
i'm trying to write this:
app.context.route = route
but it says to me that app.context doesn't exist
Here's the point of my question i'm trying to update my data that gets from my api with axios on page if route changing
like this
this the page
i'm clicking link to next page :
but when i'm route to next page, nothing happens all data is the same:
here my asyncData code:
asyncData({ app }) {
return app.$axios.$get('apps/' + app.context.route.fullPath.replace(/\/categories\/?/, ''))
.then(res => {
return {
info: res.results,
nextPage: res.next,
prevPage: res.prev
};
})
}
Thanks for your help
First thing, context.route or it's alias this.$route is immutable object and should not be assigned a value.
Instead, we should use this.$router and it's methods for programmatic navigation or <nuxt-link> and <router-link>.
As I understand, you need to render the same route, but trigger asyncData hook in order to update component's data. Only route query is changed.
Correct way to navigate to the same page but with different data is to use link of such format:
<nuxt-link :to="{ name: 'index', query: { start: 420 }}"
Then you can use nuxt provided option watchQuery on page component and access that query inside asyncData as follows:
watchQuery: true,
asyncData ({ query, app }) {
const { start } = query
const queryString = start ? `?start=${start}` : ''
return app.$axios.$get(`apps/${queryString}`)
.then(res => {
return {
info: res.results,
nextPage: res.next,
prevPage: res.prev
}
})
},
This option does not require usage of middleware. If you want to stick to using middleware functions, you can add a key to layout or page view that is used. Here is an example of adding a key to default layout:
<nuxt :key="$route.fullPath" />
This will force nuxt to re-render the page, thus calling middlewares and hooks. It is also useful for triggering transitions when switching dynamic routes of the same page component.