My problem is the firebase request on asyncData method on Nuxt. When the app is on the client the request works but the first load doesn´t work. So, on the server, have i install anything?
This is my code:
<script>
import { db } from '~/plugins/firebase.js'
export default {
asyncData (context) {
let listUsers = []
db.collection('users').get()
.then(doc => {
doc.forEach(user => {
listUsers.push({ id: user.id, ...user.data() })
});
})
return {
dataUsers: listUsers
}
}
}
</script>
The problem I see as #Andrew1325 noted, is that you do NOT return a promise from asyncData. This means that the server will NOT wait for the request to finish before sending HTML to the client.
On the other hand, how do you access your dataUsers in the component?
I suggest you to reformat the code to dispatch an action
<script>
import { db } from '~/plugins/firebase.js'
export default {
asyncData ({store}) {
return store.dispatch('FETCH_USERS')
}
}
</script>
And the a simple action
async function FETCH_USERS = ({commit}) => {
const doc = await db.collection('users').get()
commit('SET_USERS', doc)
}
Related
I have an Axios API call that works perfectly on a Vue page. I need to make it a stand-alone callable module to be re-used multiple times in the app. Every attempt has failed and I am not sure if it's lack of experience with a stand-alone js or something else.
Here is the working vue code.
<template>
<div>
<ul v-if="posts && posts.length">
<li v-for="post of posts">
<p><strong>{{post.resID}}</strong></p>
<p>{{post.Name}}</p>
</li>
</ul>
<ul v-if="errors && errors.length">
<li v-for="error of errors">
{{error.message}}
</li>
</ul>
</div>
</template>
<script>
import axios from 'axios';
export default {
name: "GetMxList",
data() {
return {
posts: [],
errors: []
}
},
// Fetches posts when the component is created.
created() {
axios.get("http://localhost:8080/rest/...")
.then(response => {
// JSON responses are automatically parsed.
this.posts = response.data
})
.catch(e => {
this.errors.push(e)
})
}
}
</script>
Vue 3. Thank you for the answer. Sorry I was not clear. My goal is to create a module (like the rest.js) and then consume it in Pinia. The intent is to load once and then use the results often. Currently it works with a "static" load like the following code where the getJSONList calls a js module that returns a JSON formatted answer and puts that answer in MyList for use throughout the app. So the components just use Pinia mapping.
actions: {
async fetchList() {
const data = await getJSONList();
this.Mylist = data;
},
Many iterations. While this doesn't return aything it at least does not throw any errors...
import axios from 'axios';
export function getJSONList() {
const rest = axios.create({
baseURL: "http://localhost:8080/rest/", // better still, use env vars
});
const getPosts = async () => {
try {
return (await rest.get("http://localhost:8080/rest/")).data;
} catch (err) {
console.error(err.toJSON());
throw new Error(err.message);
}
};
return (getPosts);
}
Typically you just need to move the Axios parts into a module and leave the consumption of the data to your components.
// services/rest.js
import axios from "axios";
const rest = axios.create({
// better still, use env vars to define your URLs
baseURL: "http://localhost:8080/rest/tctresidents/v1",
});
// This is a function
export const getResidents = async () => {
try {
// the request path will be appended to the baseURL
return (await rest.get("/Residents")).data;
} catch (err) {
// see https://axios-http.com/docs/handling_errors
console.error(err.toJSON());
throw new Error(err.message);
}
};
Then in your components / store / literally anywhere...
import { getResidents } from "./path/to/services/rest";
export default {
data: () => ({ residents: [], errors: [] }),
async created() {
try {
this.residents = await getResidents();
} catch (err) {
this.errors.push(err);
}
},
};
I am implementing firebase authentication to Nuxt js application and I am so close. The problem is I want to commit a vuext mutation inside firebase's default function onAuthStateChanged(). But when ever I load the page it shows the following error:
"Uncaught (in promise) TypeError: Cannot read properties of undefined (reading '$store')"
Can you guys please help me out with this problem.
Thanks.
import firebase from '#/plugins/firebase'
import {
getAuth,
signInWithEmailAndPassword,
onAuthStateChanged
} from "firebase/auth"
export const state = () => ({
user: null,
authIsReady: false
})
export const mutations = {
updateUser(state, payload) {
state.user = payload
console.log('user is updated', state.user)
},
setAuthIsReady(state, payload) {
state.authIsReady = payload
console.log(state.authIsReady)
}
}
export const actions = {
async signIn(context, {
email,
password
}) {
console.log('sign in action')
const res = await signInWithEmailAndPassword(getAuth(), email, password)
if (res) {
context.commit('updateUser', res.user)
} else {
throw new Error('could not complete sign in')
}
}
}
// this function is causing the problem
const unsub = onAuthStateChanged(getAuth(), (user) => {
this.$store.commit('updateUser', user)
unsub()
})
The firebase.js file that I'm importing "auth" from below, is just all the regular setting up Firebase in Nuxt stuff... and the important lines are:
const auth = getAuth()
export { auth }
Try the code below ... I have mine in a file named "fireauth.js" in the plugins folder (don't forget to import the "fireauth.js" file in your nuxt.config.js)
import {
auth
} from "~/plugins/firebase.js";
export default (context) => {
const {
store
} = context
return new Promise((resolve, reject) => {
auth.onAuthStateChanged((user) => {
if (user) {
return resolve(store.dispatch('onAuthStateChangedAction', user))
}
return resolve()
})
})
}
In your store/index.js file add the following async function in your actions setting:
async onAuthStateChangedAction(vuexContext, authUser) {
if (!authUser) { //in my case I'm just forcing user back to sign in page, only authorized users allowed//redirect from here this.$router.push({
path: '/signin',
})
}else {
//call your commits or do whatever you want to do
vuexContext.commit("setUser", authUser.email);
}
},
The first part of the code ensures that when the auth state changes in Firestore, this change is communicated to the action that you just created in the store. The second part of the code, the async function in the store accomplishes whatever you want it to do within the store.
I found something about this bug I explained at end;
Component codes
async fetch(){ await this.$store.dispatch('bots/getBots') },
computed: { ...mapState('bots', ['bots']) },
Store codes
export const state = () => {
return {
bots: []
}
}
export const mutations = {
UPDATE_BOTS(state, bots) {
state.bots = bots
}
}
export const actions = {
getBots({commit}) {
this.$axios.$get('url', {headers: {uid: '12345'}})
.then(res => {
commit('UPDATE_BOTS',res.robots)
})
.catch(e => {
console.log(e)
})
}
}
Issue: When moving between pages via nuxt-link data loads perfectly but when I reload the page bots state is empty...
Found Issue:
I use nuxt-auth and I had one plugin for checking status of axios request that if it was 401 unauthorized I logout user if he was loggedIn, So status undefined error was from here but I commented the plugin codes and I got other error from nuxt-auth that causes that problem I had So I related that issue in other question u can see it here:
Nuxt-Auth Bug: Looks for autherization in any get request that has headers config
It is the expected behavior. Vuex state is kept in memory and when you reload the page it gets purged.
Instead of this state
export const state = () => {
return {
bots: []
}
}
try this
export const state = () => ({
bots: []
})
so I call Stripe.card.createToken in my api.js file and want to return the token this function generates so I can use it in my vuex, how can I do that?
// api.js
export const stripeToken = async ({ cardInfo }) => {
const { data } = await Stripe.card.createToken({
cardInfo,
});
return data;
};
So I want to use it in my actions in vuex like this. I did this and it doesn't work, it returns undefined:
//vuex
import { stripeToken } from '#src/store/api';
async stripeToken({ dispatch }, { cardInfo }) {
const { data } = await stripeToken({ cardInfo });
console.log('tokenId: ', data.tokenId);
},
I'm not familiar with vuex, but the Stripe.card.createToken method takes two parameters: a JavaScript object containing credit card data entered by the user, and a callback function to handle the response. You can learn more about it in the Stripe documentation here.
Here's how you could display the ID of a token with Stripe.card.createToken:
Stripe.card.createToken(cardInfo, (status, response) => {
if (response.error) {
console.log(response.error);
} else {
console.log(response.id);
}
});
Note that Stripe.card.createToken is an old method from Stripe.js v2 that is now deprecated, so I would recommend upgrading to Stripe.js v3 if possible.
I'm attempting to add an Axios plugin to Nuxt as described here, but it doesn't seem to work.
This is my plugins/axios.js file...
export default function({ $axios }) {
console.log('Im in the axios plugin')
$axios.defaults.baseURL = `https://localhost:5001/api`
$axios.defaults.headers = {
Accept: 'application/json',
'Content-Type': 'application/json'
}
$axios.onRequest((config) => {
console.log('Making request to ' + config.url)
})
}
This is my nuxt.config.js
plugins: ['~/plugins/axios'],
modules: ['#nuxtjs/axios']
And this is where I use Axios in a file called services/BookService.js:
import axios from 'axios'
export default {
getBooks() {
return axios.get('/Home')
},
getBooksFiltered(payload) {
return axios.post('/Home/Filters', payload)
}
}
I get the console.log('Im in the axios plugin') from within my plugin, but nothing else. $axios.onRequest doesn't appear to run, and the baseURL doesn't appear to be set correctly when getBooksFiltered is triggered. I get a 404 when it tried to hit the address http://localhost:3000/Home/Filters. As described in my plugin, the address should be https://localhost:5001/api/Home/Filters
I've also tried the following in my nuxt.config.js, but it doesn't work:
axios: {
baseURL: 'https://localhost:5001/api'
}
Any ideas?
Edit
I've modified my services/BookService.js based on the suggestion below to the following...
export default {
getBooks(axios) {
console.log('Im in getBooks')
return axios.get('/Home')
}
}
My action request that makes my api call is the following....
import BookService from '~/services/BookService.js'
export const fetchBooks = (context) => {
console.log('Im in fetchBooks action')
return BookService.getBooks(this.$axios)
.then((response) => {
context.commit('SET_BOOKS', response.data.booksList)
})
.catch((error) => {
console.log(error)
})
}
And my method in my component that calls the actions...
async fetch({ store, error }) {
try {
console.log('Im in index -> fetch')
await store.dispatch('fetchBooks')
} catch (e) {
error({
statusCode: 503,
message: 'Unable to fetch books at this time'
})
}
}
I'm aware that I may be mixing async/await with promises incorrectly but I don't believe it's the cause of this issue.
Console returns the following...
My network tab contains a single request to http://localhost:3000/ which seems incorrect. It should be https://localhost:5001/api/Home based on the plugin and the address specified in the action. It is also never entering $axios.onRequest
The axios-module sets up an Axios instance on the Nuxt app instance. When you import Axios from axios, and use it directly, you're not using the previously setup Axios instance.
To fix the issue, you could either reference the preconfigured Axios instance from window.$nuxt.$axios (only in the browser), or setup your service to take an Axios instance as a parameter:
// services/BookService.js
export default axios => ({
getBooks() {
return axios.get('/Home')
},
getBooksFiltered(payload) {
return axios.post('/Home/Filters', payload)
}
})
// store.js
import BookService from '~/services/BookService.js'
export default {
actions: {
async getBooks({ commit }) {
const books = await new BookService(this.$axios).getBooks()
commit('SET_BOOKS', books)
}
}
}
Another solution from nuxt-community/axios-module #28:
~/plugins/axios-port.js
import { setClient } from '~/services/apiClient'
export default ({ app, store }) => {
setClient(app.$axios)
}
~/services/apiClient.js
let client
export function setClient (newclient) {
client = newclient
}
// Request helpers
const reqMethods = [
'request', 'delete', 'get', 'head', 'options', // url, config
'post', 'put', 'patch' // url, data, config
]
let service = {}
reqMethods.forEach((method) => {
service[method] = function () {
if (!client) throw new Error('apiClient not installed')
return client[method].apply(null, arguments)
}
})
export default service
Use:
import apiClient from '~/services/apiClient'
export default {
async current () {
return apiClient.get('...')
}
}
In my case I exported a customized axios instance as the doc suggested in my axios.js
export default function ({ $axios }, inject) {
const api = $axios.create({
baseURL:'/api'
})
// Inject to context as $api
inject('api', api)
}
Then use this.$api.get or this.$api.post in your getBook service
The above one works for me
As I have just tested, in each request we should use $axios.
Example: this.$axios.get('....'), or in another context this.$nuxt.$axios.get('...');
Because axios extension use with the app context instance, if we import, it will create a new instance which plugin cannot extend.
I have put test code on stackblitz: here
It seems you need to yarn add #nuxtjs/axios or npm install #nuxtjs/axios like the setup instruction here before it can work: https://axios.nuxtjs.org/setup
I haven't experienced with nuxt yet but I don't think by adding some line of code into some js file without actually installing will make the package available into your repo.