json fetch in vue not working as it should - javascript

I'm building a simple test for a project of mine after watching a bunch of vue.js lessons.
My test is simple, fetch some json from an api i created with express.js and output it on the screen, I wanted do it the proper way so i build all the little components that make up my test homepage and commited the fetch through dispatching an action, this is the structure:
my submissions/action.js
export default {
async loadSubs(context) {
const res = await fetch(`http://localhost:3001/api`, {
mode: "no-cors",
});
console.log(res);
const resData = await res.json();
console.log(resData.name);
if (!res.ok) {
console.log(resData);
const error = new Error(resData.message || "failed to fetch");
throw error;
}
const subsList = [];
for (const key in resData) {
const sub = {
name: resData[key].name,
};
subsList.push(sub);
}
context.commit("setSubs", subsList);
},
};
my submission/mutation.js:
export default {
setSubs(state, payload) {
state.submissions = payload;
},
};
they get imported in submissions/index.js :
import mutations from "./mutations.js";
import actions from "./actions.js";
import getters from "./getters.js";
export default {
namespaced: true,
state() {
return {
submissions: [
{
name: "",
},
],
};
},
mutations,
actions,
getters,
};
and submission/index.js gets imported in store/index.js
import { createStore } from "vuex";
import SubmissionsModule from "./modules/Submissions/index.js";
const store = createStore({
modules: {
submissions: SubmissionsModule,
},
});
export default store;
My vue components are the following(i'm leaving out the css)
BaseCard.vue
<template>
<div class="card">
<slot></slot>
</div>
</template>
my Submission/SingleSubmission.vue
<template>
<div class="subList">
<base-card>
<h2>{{ name }}</h2>
</base-card>
</div>
</template>
<script>
export default {
props: ["name"],
};
</script>
and this is my views/Home.vue:
<template>
<div>
<single-submission></single-submission>
</div>
</template>
<script>
import SingleSubmission from "../components/SingleSubmission.vue";
export default {
components: {
SingleSubmission,
},
computed: {},
created() {
this.loadSubmission();
},
methods: {
async loadSubmission() {
//this.isLoading = true;
try {
await this.$store.dispatch("submissions/loadSubs");
} catch (error) {
this.error = error.message || "something went wrong";
}
// this.isLoading = false;
},
},
};
</script>
The api is just sending back a line of json, just to test if i can render something.
const express = require("express");
const app = express();
const apiPrefix = "/api";
//Define the root endpoint
app.get(apiPrefix, async (req, res) => {
console.log(req.headers);
res.json({
name: "test",
});
});
app.listen(3001, () => {
console.log("listening on port 3001");
});
RESULTS OF WHAT I'VE DONE:
this is the result of res object when i console log it:
and this is the network tab on the browser i use:(fun fact:on Brave and Chrome the response tab is empty, while on firefox, i can see the json intended to see, but only in the developer tools reponse tab )
In the end the response status is 200 but i get nothing from the fetch expept res but console.log(resData.name) don't even gets executed and nothing is printed on the screen.
I really don't know what to do because it's seems such a stupid thing and I can't get around it.

Related

How can I send an api PATCH request with only the fields that have been modified from my vue application?

I have a vue application with a form that fetches data from my backend api. My api has an endpoint that accepts PATCH requests and when using postman to test it works great.
When I try from my vue app it always sends every field, even if the field hasn't been modified. I realize it does this because I am using v-model for each form field to a pinia state object and I'm sending the entire object during the PATCH request. I really only want to send the fields that have been modified. How can I cleanly accomplish this?
Here is my component for my form:
// #/components/ApplicationForm.vue
<template>
<input v-model="application.name" class="input" type="text" />
<input v-model="application.location" class="input" type="text" />
</template>
<script setup>
import { useRoute } from "vue-router";
import { useApplicationStore } from "#/stores/ApplicationStore";
import { storeToRefs } from "pinia";
const route = useRoute();
const { applications, application, error } = storeToRefs(useApplicationStore());
const { updateApplication } = useApplicationStore();
</script setup>
I use a pinia action that uses a composable to get and update the data for the form. Here is my store:
// #/stores/ApplicationStore.js
import { defineStore } from "pinia";
import router from "#/router";
import { getApplications, updateApplication } from "#/composables/applications";
export const useApplicationStore = defineStore("application", {
state: () => ({
applications: [], // all applications
application: {}, // selected application to create or modify
loading: false,
success: "",
error: "",
}),
actions: {
async fetchApplications() {
this.loading = true;
this.applications = [];
const { applications, error } = await getApplications();
this.applications = applications;
this.error = error;
this.loading = false;
},
async updateApplication() {
this.loading = true;
this.success = "";
const { application, results, error } = await updateApplication(this.application);
this.application = application;
this.error = error;
this.loading = false;
if (results.status === 200) {
this.success = "Successfully Updated";
}
}
}
});
Here is the function from my composable that runs the update and sends the PATCH request:
// #/composables/applications.js
import axios from "axios";
let applications = [];
let application = {};
let error = "";
const url = process.env.VUE_APP_API_URL + "api/applications";
export const updateApplication = async (payload) => {
let results = {};
try {
error = "";
results = await axios.patch(`${url}/${payload.id}`, payload, { responseType: "json" });
application = results.data;
} catch (err) {
console.log("inside catch");
error = err;
}
return { application, results, error };
};
I feel like this should be easier than I'm anticipating. I'm new to using pinia and sending these patch requests. I don't know if it's exhaustion, or just my poverty of knowledge preventing me from clearly solving this. Many thanks.

Next.js GetStaticPaths: ReferenceError: Cannot access 'getAllPostIds' before initialization

I am making a simple next js blog type of application using graphql as a data fetching backend to render text. Using getStaticPaths, I'm running into the following error when I try to fetch data for my page.
ReferenceError: Cannot access 'getAllPostIds' before initialization
Here is my code:
pages/posts/[id].tsx
import { getAllPostIds } from '../../../lib/posts'
const Post = ({ postData }) => {
... code.....
}
export const getStaticPaths = async () => {
const paths = getAllPostIds('aws');
return {
paths,
fallback: false
}
}
export default Post;
And here is my posts.ts where I use graphql to fetch data.
import { useQuery } from "react-query";
import { GraphQLClient } from "graphql-request";
const GET_POST_IDS = gql`
query($folder: String!) {
repository(owner: "assembleinc", name: "documentation") {
object(expression: $folder) {
... on Tree {
entries {
name
}
}
}
}
}`
;
const graphQLClient = new GraphQLClient('https://api.github.com/graphql', {
headers: {
Authorization: `Bearer ${process.env.GITHUB_ACCESS_TOKEN}`
}
});
export const getAllPostIds = (folder: String) => {
return useQuery(folder, async () => {
... fetch data ...
});
}
Essentially, before I can even get the data through graphql, next js is complaining that getAllPostIds can't be initialized even though I import it at the top. Is there some next.js magic that I am not seeing?

undefined getStaticProps causes build failure on page that doesn't exist

I'm fetching page data from a cms, so far I have only one page in pages/posts.
pages/posts/[slug].js
import { getAllPostsWithSlug, getPostAndMorePosts } from '../../lib/api';
export default function Post({ post }) {
const router = useRouter();
const { slug } = router.query;
return (
<div>
<p>
title: {typeof post == 'undefined' ? 'no post' : post.title}
</p>
</div>
);
}
export async function getStaticProps({ params, preview = null }) {
const data = await getPostAndMorePosts(params.slug, preview);
const content = await markdownToHtml(data?.posts[0]?.content || '');
return {
props: {
preview,
post: {
...data?.posts[0],
content,
},
morePosts: data?.morePosts,
},
};
}
export async function getStaticPaths() {
const allPosts = await getAllPostsWithSlug();
return {
paths: allPosts?.map((post) => `/posts/${post.slug}`) || [],
fallback: true,
};
}
That will correctly display post.title, but if I access the property directly with
<p>title: {post.title}</p>
I get the build error:
post undefined
Is next trying to build a page out of the template with no data? When the build succeeds I only have one route in /posts.

VueJS Component failing to render when fetching data

I'm new to Vue.JS and JavaScript, so I have awful times debugging these applications, specially with promises and asynchronous tools. I'm trying to build my first Vue component that fetches data from somewhere. I'm using the Google Sheets API and returning some cells of a sample sheet. My component looks like this:
<template>
<ul>
<li v-for="athlete in athletes" :key="athlete">
{{ athlete }}
</li>
</ul>
</template>
<script>
import readCopaPinheiros from '#/sheets/fetchData.js';
export default {
name: 'AthletesTable',
data () {
return {
loading: false,
athletes: null
}
},
created () {
this.fetchData()
},
methods: {
fetchData() {
this.loading = true;
readCopaPinheiros('inscritos').then(values => {
this.loading = false;
console.log(values)
this.athletes = values
});
},
}
}
</script>
<style>
</style>
EDIT 1
The fetchData script:
const fs = require('fs');
const { google } = require('googleapis');
const TOKEN_PATH = '';
const CREDENTIALS_PATH = ''
const credentials = JSON.parse(fs.readFileSync(CREDENTIALS_PATH, 'utf-8'));
const {
client_secret: clientSecret,
client_id: clientId,
redirect_uris: redirectUris,
} = credentials.installed;
const oAuth2Client = new google.auth.OAuth2(
clientId, clientSecret, redirectUris[0],
);
const token = fs.readFileSync(TOKEN_PATH, 'utf-8');
oAuth2Client.setCredentials(JSON.parse(token));
async function readSheet(spreadsheetId, range) {
const sheets = google.sheets({ version: 'v4', auth: oAuth2Client });
return sheets.spreadsheets.values.get({
spreadsheetId,
range,
})
.then(res => res.data.values)
.catch(err => console.log('Opa! Erro:', err));
}
function readSheetJsnofied(spreadsheetId, range) {
return readSheet(spreadsheetId, range)
.then(values => jsonifySheet(values));
}
function jsonifySheet(sheetValues) {
const header = sheetValues[0];
const values = sheetValues.slice(1);
return values.map((row) => {
const rowObj = ({});
for (let i=0; i < row.length; i++) rowObj[header[i]] = row[i];
return rowObj;
});
}
const readCopaPinheiros = d => readSheetJsnofied('sheetId', d);
export default readCopaPinheiros
For some reason the component doesn't render. I don't know what to do even to debug, all my console log tries never prints something to the console. Could someone help me understand what is going wrong?
EDIT 2
This error just shows up when trying to fetch data:
When I try to use a placeholder list with fake values directly in the data function it works. I don't believe that is a problem with the rendering itself, but how it interacts with the created and fetchData functions.
v-for="athlete in athletes"
This code only works when the athletes is an array. Initially, you set it as null so until the data from api is arrived, it will be null.
But the component still tries to render the component with your null athletes and will make the error.
You can try with this solution:
data () {
return {
loading: false,
athletes: []
}
},

How can i test an API call in vuejs using jest?

im having this method in my component that makes an API call with axios, I checked the docs on how to test it but I cant seem to figure out how to do so. Any help would be appreciated.
loadContents() {
axios.get('/vue_api/content/' + this.slug).then(response => {
this.page_data = response.data.merchandising_page
}).catch(error => {
console.log(error)
})
},
You could use moxios or axios-mock-adapter to automatically mock Axios requests. I prefer the latter for developer ergonomics.
Consider this UserList component that uses Axios to fetch user data:
// UserList.vue
export default {
data() {
return {
users: []
};
},
methods: {
async loadUsers() {
const { data } = await axios.get("https://api/users");
this.users = data;
}
}
};
With axios-mock-adapter, the related test stubs the Axios GET requests to the API URL, returning mock data instead:
import axios from "axios";
const MockAdapter = require("axios-mock-adapter");
const mock = new MockAdapter(axios);
import { shallowMount } from "#vue/test-utils";
import UserList from "#/components/UserList";
describe("UserList", () => {
afterAll(() => mock.restore());
beforeEach(() => mock.reset());
it("loads users", async () => {
mock
.onGet("https://api/users")
.reply(200, [{ name: "foo" }, { name: "bar" }, { name: "baz" }]);
const wrapper = shallowMount(UserList);
await wrapper.vm.loadUsers();
const listItems = wrapper.findAll("li");
expect(listItems).toHaveLength(3);
});
});
demo

Categories

Resources