I'm trying to display countries from database which the continentId foreach country == logged user scope.
An user scope is between 1-5.
here is my vue template
<div class="container w-75" v-show="showGrid">
<div class="row" style="width:900px; height:900px; padding-left:200px">
<div class="col-md-4" v-for="country of countries" v-bind:key="country">
<div v-if="country.continentId==setup" class="card p-3" style="cursor:pointer">
<router-link :to="{ path: '/FetchData', query: { query: country.countryName}}">
<div class="d-flex flex-row mb-3">
<div class="d-flex flex-column ml-2"><span>{{country.countryId}}</span></div>
</div>
<h6 style="text-align:left">{{country.countryName}}</h6>
</router-link>
</div>
and those are my methods
export default {
methods: {
async getCountries() {
let country = this.$route.query.query
if (!country) {
await axios
.get("https://localhost:44391/api/Pho/GetCountries")
.then((res) => (this.countries = res.data))
} else {
await axios
.get(
"https://localhost:44391/api/Pho/GetCountries?country=" +
this.$route.query.query
)
.then((res) => (this.countries = res.data))
this.searchbar = false
}
},
async setup() {
let token = "??"
const scope = ref("")
const response = await fetch(
"https://localhost:44391/api/Auth/UserScope",
{
headers: {
"Content-Type": "application/json",
Authorization: "Bearer " + token,
},
credentials: "include",
}
)
const content = response.json()
scope.value = `${content.scope}`
return {
scope,
}
},
},
async mounted() {
this.setup()
await this.getCountries()
},
}
the method setup return le scope of the logged user and the getCountries method returns the list of countries.
when i inspect i find that the scope is returned but the <div v-if="country.continentId==setup" class="card p-3" style="cursor:pointer"> condition does not work properly.
The setup() hook is not supposed to be under methods. It needs to be at the top level of the object:
export default {
methods: {
// setup() { /*...*/ } ❌
},
setup() { /*...*/ }, ✅
}
Also, don't make the setup() hook async unless you're intentionally making it an async component, which requires a <Suspense> as an ancestor. Instead, move the asynchronous code into its own function within:
export default {
setup() {
const scope = ref('')
const fetchUserScope = async () => {
const response = await fetch(/*...*/)
const content = await response.json()
scope.value = content.scope
}
fetchUserScope()
return { scope }
}
}
Also, you can't invoke the setup() hook from the template like that. You're really just trying to compare continentId to the scope value, so use scope directly:
<!-- <div v-if="country.continentId == setup"> --> ❌
<div v-if="country.continentId == scope"> ✅
You shouldn't try to invoke setup() from mounted() hook either. Vue controls the lifecycle hooks itself:
export default {
mounted() {
// this.setup() ❌
}
}
Related
I stumbled into a totally unexpected problem while refactoring my code to composition API: there doesn't seem to be any (documented) way of accessing current instance from the lifecycle hooks.
sample code:
import { defineComponent, onMounted } from 'vue';
export default defineComponent({
setup() {
onMounted(() => {
console.log(this); // <-- will be undefined
});
},
mounted() {
console.log(this); // <-- will be the component
},
}
I've spent hours trying to find a solution to this and ultimately just used the old options API to get what I want. None of examples, tutorials or documentation - that I read - use this in the hooks.
But I find it unbelievable that only undocumented getCurrentInstance would be the way to get the current instance from the hook.
So, which doc did I miss?
UPDATE
Here is the same example with a component
const { createApp, ref, onMounted } = Vue;
const MyComponent = {
setup() {
const id = ref(Math.round(Math.random() * 100000));
const count = ref(0);
const plus = () => { count.value++; }
const minus = function() { count.value--; }
onMounted(() => {
count.value = Math.round(Math.random() * 10)
});
return {id, count, plus, minus }
},
template: `id: {{id}} <button type="button" #click="minus()">-1</button>
{{count}}
<button type="button" #click="plus()">+1</button><hr/>`
}
const App = {
components: {
MyComponent
}
}
const app = createApp(App)
app.mount('#app')
<div id="app">
<my-component v-for="i in 5" />
</div>
<script src="https://unpkg.com/vue#3/dist/vue.global.prod.js"></script>
What for do you need this in the component?
If you create your component with Composition API, then you can access all the properties directly, without using this.
Here is a very basic example:
const { createApp, ref, onMounted } = Vue;
const App = {
setup() {
const count = ref(0);
const up = () => { count.value++; }
const down = function() { count.value--; }
onMounted(() => {
count.value = 10
});
return {count, up, down }
}
}
const app = createApp(App)
app.mount('#app')
<div id="app">
<button type="button" #click="down()">-1</button>
{{count}}
<button type="button" #click="up()">+1</button>
</div>
<script src="https://unpkg.com/vue#3/dist/vue.global.prod.js"></script>
I am trying to render a name to my component, which I get from an axios response. I am able to print the name in the console but {{username}} is never updated.
setup() {
const state = reactive({
username: '',
})
const submit = async () => {
try {
const response = await api.getTest()
if (response != null) {
state.username = response.name
console.log("I am the state " + state.username)
}
} catch (error) {
console.log('Error while getting the response:', error)
}
}
return {
...state,
submit
}
},
template
<template>
<button v-on:click="submit()" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
Button
</button>
<div class="text-white">
Name: {{username}}
</div>
</template>
why is the username not updating?
is this the preferred way to do this?
You are using a reactive object, so you have to use that object in the template. username is not defined in the template scope, it would be {{state.username}}.
One other approach would be to define the username as a ref, but then you have to set it's value:
const username = ref('');
And in the async function:
username.value = response.name
I'm trying to deploy my NextJs app (using GraphCMS) on Vercel. When I build the app on my computer it works fine, I can build and run the app locally but once I try to deploy the same exact app on Vercel it crash with this error
TypeError: Cannot read properties of undefined (reading 'document')
at Object.parseRequestExtendedArgs (/vercel/path0/node_modules/graphql-request/dist/parseArgs.js:37:25)
at /vercel/path0/node_modules/graphql-request/dist/index.js:422:42
at step (/vercel/path0/node_modules/graphql-request/dist/index.js:63:23)
at Object.next (/vercel/path0/node_modules/graphql-request/dist/index.js:44:53)
at /vercel/path0/node_modules/graphql-request/dist/index.js:38:71
at new Promise ()
at __awaiter (/vercel/path0/node_modules/graphql-request/dist/index.js:34:12)
at request (/vercel/path0/node_modules/graphql-request/dist/index.js:418:12)
at getPosts (/vercel/path0/.next/server/chunks/104.js:1143:82)
at getStaticPaths (/vercel/path0/.next/server/pages/post/[slug].js:98:86)
Build error occurred
Error: Failed to collect page data for /post/[slug]
at /vercel/path0/node_modules/next/dist/build/utils.js:959:15
at processTicksAndRejections (node:internal/process/task_queues:96:5) {
type: 'Error'
}
error Command failed with exit code 1.
I don't understand where this is coming from.
pages/post/[slug].js
import React from "react";
import { useRouter } from "next/router";
import {
PostDetail,
Categories,
PostWidget,
Author,
Comments,
CommentsForm,
Loader,
} from "../../components";
import { getPosts, getPostDetails } from "../../services";
import { AdjacentPosts } from "../../sections";
const PostDetails = ({ post }) => {
const router = useRouter();
if (router.isFallback) {
return <Loader />;
}
return (
<>
<div className="container mx-auto px-10 mb-8">
<div className="grid grid-cols-1 lg:grid-cols-12 gap-12">
<div className="col-span-1 lg:col-span-8">
<PostDetail post={post} />
<Author author={post.author} />
<AdjacentPosts slug={post.slug} createdAt={post.createdAt} />
<CommentsForm slug={post.slug} />
<Comments slug={post.slug} />
</div>
<div className="col-span-1 lg:col-span-4">
<div className="relative lg:sticky top-8">
<PostWidget
slug={post.slug}
categories={post.categories.map((category) => category.slug)}
/>
<Categories />
</div>
</div>
</div>
</div>
</>
);
};
export default PostDetails;
// Fetch data at build time
export async function getStaticProps({ params }) {
const data = await getPostDetails(params.slug);
return {
props: {
post: data,
},
};
}
// Specify dynamic routes to pre-render pages based on data.
// The HTML is generated at build time and will be reused on each request.
export async function getStaticPaths() {
const posts = await getPosts();
return {
paths: posts.map(({ node: { slug } }) => ({ params: { slug } })),
fallback: false,
};
}
here is the Graphql query getPosts
export const getPosts = async () => {
const query = gql`
query MyQuery {
postsConnection {
edges {
cursor
node {
author {
bio
name
id
photo {
url
}
}
createdAt
slug
title
excerpt
displayedDate
featuredImage {
url
}
categories {
name
slug
}
}
}
}
}
`;
const result = await request(graphqlAPI, query);
return result.postsConnection.edges;
};
getPostDetails
export const getPostDetails = async (slug) => {
const query = gql`
query GetPostDetails($slug: String!) {
post(where: { slug: $slug }) {
title
excerpt
featuredImage {
url
id
}
author {
name
bio
photo {
url
}
}
createdAt
slug
content {
raw
}
categories {
name
slug
}
displayedDate
}
}
`;
const result = await request(graphqlAPI, query, { slug });
return result.post;
};
I really don't understand why I can build it locally but not en Vercel, Thanks
Tried to modify queries, turn off fallback and others things that did not work
I get back 500 errors if i try to send a file from Vue to my API endpoint in .net Core
I followed tutorials who do this, but they do not seem to work for this setup.
.net core API:
[Route("api/[controller]")]
[ApiController]
public class FileUploadController : ControllerBase
{
[HttpPost("[Action]")]
public string sendFiles([FromBody]FileUploadAPI file)
{
return "Yes!";
}
public class FileUploadAPI
{
public IFormFile File { get; set; }
}
}
Vue:
this.$axios.post(
'https://localhost:44352/api/fileupload/sendFiles',
event.target.files[0],
)
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
I want to receive my file in the API
Request failed with status code 500
You would get a 404 error because you're using the wrong URL.
Your action name is sendFiles (plural) so the correct URL path would be /api/FileUpload/sendFiles.
Axios is capable of handling FormData correctly as a multipart/form-data request. You do not need to set headers (which were incorrect anyway), nor should you wrap the data in an object.
let data = new FormData();
data.append('file', files[0]); // assuming "files" refers to a FileList
this.$axios.post('https://localhost:44352/api/FileUpload/sendFiles', data)
.then(...)
Following example code snippet may be help for you. In it I am using vuetify, vue-upload-component and axios to upload an image.
<template lang="html">
<div class="imageUploader">
<!-- <v-card> -->
<!-- <div v-show="$refs.upload && $refs.upload.dropActive" class="drop-active"></div> -->
<div class="avatar-upload">
<div class="text-center p-2">
<div class="avatar-container">
<div class="no-image" v-if="files.length === 0 && file == ''">
<v-icon>cloud_upload</v-icon>
</div>
<template v-else>
<img :src="file" alt="">
</template>
</div>
</div>
<div class="text-center p-2">
<v-btn class="browse-btn" flat>
<file-upload
extensions="gif,jpg,jpeg,png,webp"
accept="image/png,image/gif,image/jpeg,image/webp"
name="avatar"
v-model="files"
#input="uploadImage"
ref="upload">
Choose File
</file-upload>
</v-btn>
</div>
</div>
<!-- </v-card> -->
</div>
</template>
<script>
import Cropper from 'cropperjs'
import VueUploadComponent from 'vue-upload-component'
//import axios from 'axios'
export default {
components: {
'file-upload': VueUploadComponent
},
props: ['order', 'imageURL'],
data() {
return {
dialog: false,
files: [],
edit: false,
cropper: false,
file: '',
}
},
mounted() {
if (this.imageURL) {
this.file = this.$baseURL+'document/downloadimage/' + this.imageURL
}
},
watch: {
imageURL() {
if (this.imageURL) {
this.file = this.$baseURL+'document/downloadimage/' + this.imageURL
}
},
},
methods: {
**uploadImage(file) {
let formData = new FormData();
formData.append('file', file[0].file);
axios.post(axios.defaults.baseURL + 'document/uploadimage', formData, {headers: {'Content-Type': 'multipart/form-data'}})
.then((response) => {
this.dialog = false
this.$emit('upload', {id: response.data.result[0].objectId, order: this.order})
this.file = this.$baseURL+'document/downloadimage/' + response.data.result[0].objectId
let reader = new FileReader()
reader.readAsDataURL(file[0].file)
reader.onload = () => {
let base64 = reader.result.split(',')[1]
this.$emit('base64', base64)
}
this.getDimensions(this.$baseURL+'document/downloadimage/' + response.data.result[0].objectId, (result) => {
this.$emit('dimensions', {width: result.width, height: result.height})
})
})
.catch((error) => {
console.log(error)
})
},**
getDimensions(url, callback) {
var img = new Image();
img.src = url
img.onload = function() {
var result = {width: this.width, height: this.height}
callback(result)
}
}
},
}
</script>
I'an not using getters.js file seperately, instead getters are written in js->assets->store->modules->user.js file
This is my user.js
const state = {
count : '',
list:[]
};
const mutations = {
COUNT: (state, data) => {
state.count = data
},
LIST : (state, data) => {
state.list = data
}
};
const getters = {
userCount:(state) => state.list.length
};
const actions = {
getList: ({commit,state}) => {
axios.get('/api/user/list')
.then((response) => {
commit('LIST', response.data);
})
}
};
export default {
namespaced: true,
state,
getters,
actions,
mutations
}
This is my user vue component-user.vue
<template>
<div class="col-lg-3 col-xs-6">
<div class="small-box bg-yellow">
<div class="inner">
<h3>{{ usercount }}</h3>
<p>User Registrations</p>
</div>
<div class="icon">
<i class="ion ion-person-add"></i>
</div>
View <i class="fa fa-arrow-circle-right"></i>
</div>
</div>
</template>
<script>
export default{
computed: {
usercount() {
return this.$store.getters['user/userCount'];
}
},
mounted(){
this.$store.dispatch('user/getList');
}
}
</script>
In user.js,
alert(state.list.length)
gives the correct count in the alert box.
But in user.vue,
alert(this.$store.getters['user/userCount'])
gives 'undefined'
remove unnecessary : from this:
const getters = {
userCount (state) => state.list.length
};
In the Api controller, I'am using paginate() instead of get().vue dev tools helped me to find out this...
getList: ({commit,state}) => {
axios.get('/api/user/list')
.then((response) => {
commit('LIST', response.data);
})
}
changed response.data to response.data.data