I did [GET] method using Axios. Everything is working fine, when I want to output i get this kind of thing:
http://prntscr.com/mpey70
This is my JS with HTML and VUE code on how I am trying to output it:
HTML, VUE:
<div class="col-lg-6">
<p>Casuals</p>
<ul>
<div v-bind:key="realsub.id+1" v-for="realsub in subnavreal">
<div v-if="nav.linkTitle == 'Male'">
<li><router-link :to="{ path: whiteSpace(realsub.male.casual) }">{{JSON.realsub.male.casual}}</router-link></li>
</div>
<div v-if="nav.linkTitle == 'Female'"></div>
<li><router-link :to="{ path: whiteSpace(realsub.female.casual) }">{{realsub.female.casual}}</router-link></li>
</div>
</ul>
</div>
And this is Related JS code:
import axios from 'axios';
import uuid from 'uuid';
export default {
name: 'navigation',
data(){
return{
subnavreal: []
}
},
props: ["navigation"],
methods:{
whiteSpace(a){
console.log(a);
}
},
async created(){
axios.get('/products.json')
.then(res => this.subnavreal = res.data)
.catch(err => console.log(err));
}
}
</script>
What I want to display is only the name of that object, for example: "Hoodies"
Any solutions? :)
I think you are printing the entire Response. You can use the Object.keys() to print the keys.
let user = {
name: "tom",
age: 20
}
If you want to print the keys [name, age] use Object.keys(user)
Related
I'm getting the error:
"Property "item" was accessed during render but is not defined on instance."
I don't know exactly why, but I was thinking maybe it's because there's an async fetch involved and the template renders before the fetch can be completed?
If that's the case, how do you fix that in Vue3 with "script setup"? I know how to handle that in React, but what is the Vue equivalent to
{item && <Component />}
? I already tried a "v-if" like this:
<div class="homescreen_products" v-if="items" :v-for="item in items">
but that doesn't fix anything. Did I do this wrong? Do I use a lifecycle hook like "beforeMount" or "mounted"? I was under the impression that this wasn't necessary with "script setup"?
If it's not the async fetch thing, did I pass the props wrong? Did I try to render the props wrong? Does it have something to do with using VueX? What causes the "...accessed during render but is not defined on instance" error?
Here's the full code:
store.js
import { createStore } from "vuex";
export default createStore({
state: {
productsArray: [],
checkoutCart: [],
},
mutations: {
ADD_ITEM_TO_CART(state, payload) {
state.checkoutCart.push(payload);
},
INIT_PRODUCTS(state, payload) {
state.productsArray = payload;
},
},
actions: {
cartAdd({ commit }, payload) {
console.log("cartAdd()/payload= ", payload);
commit("ADD_ITEM_TO_CART", payload);
},
getAllProducts({ commit }) {
fetch("http://localhost:5000")
.then((res) => res.json())
.then((data) => {
console.log("data= ", data);
commit("INIT_PRODUCTS", data);
});
},
},
getters: {
getCheckout: (state) => state.checkoutCart,
getProducts: (state) => state.productsArray,
}
});
ParentComponent.vue
<template>
<div class="homescreen">
<h2 class="homescreen_title">Latest Products</h2>
<div class="homescreen_products" :v-for="item in items">
<ChildComponent :description="item.description" />
</div>
</div>
</template>
<script setup>
import ChildComponent from "../ChildComponent/ChildComponent.vue";
import { useStore } from "vuex";
import { computed } from "vue";
const store = useStore();
const getProducts = async () => {
await store.dispatch("getAllProducts");
};
getProducts();
const items = computed(() => store.getters.getProducts);
</script>
ChildComponent.vue
<template>
<div class="product">
<img src="https://picsum.photos/200/300" alt="" />
<div class="product_info">
{{ description }}
</div>
</div>
</template>
<script setup>
// eslint-disable-next-line no-undef
defineProps({
description: String,
});
</script>
EDIT 2:
"Items" is being populated correctly. When I use this code instead of the "v-for" I have in the OP:
<div class="homescreen_products">
{{items}}
</div>
it gets rendered in the template like this:
i.imgur.com/IUjEjN5.png
EDIT 3: #monstis
When I change my ParentComponent to:
<template lang="">
<div class="homescreen">
<h2 class="homescreen_title">Latest Products</h2>
<div v-if="items">
<div class="homescreen_products" v-for="item in items">
<ProductScreen :description="item.description" />
</div>
</div>
</div>
</template>
With the
-"v-if" moved to a parent div
-"v-for" without the binding (":v-for")
it still doesn't work. I'm getting the error:
error Elements in iteration expect to have 'v-bind:key' directives vue/require-v-for-key
You don't need to use vbind with v-for but you should use the :key with v-for. And you should use the onMounted with script setup.
HelloWorld.vue
import axios from "axios";
export const router = () => axios.get("https://fakestoreapi.com/products");
<template>
<div>
<div v-for="item in items" :key="item.id">
<b> id: {{ item.id }}</b>
<router-link
:to="`/${item.id}`"
>
{{ item.title }}
</router-link>
</div><!-- end v-for -->
<router-view></router-view>
</div>
</template>
<script>
import { router } from "./router";
export default {
name: "HelloWorld",
components: {},
data() {
return {
items: [],
};
},
mounted() {
router().then((r) => {
this.items = r.data;
});
},
};
</script>
User.vue
import axios from "axios";
export const routerid = (itemId) =>
axios.get("https://fakestoreapi.com/products/" + itemId);
<template>
<div>
<div v-if="item">
<h1>Price: {{ item.price }}</h1>
</div>
<tabs />
</div>
</template>
<script>
import { routerid } from "./routerid";
import tabs from "./tabs";
export default {
name: "User",
components: {
tabs,
},
data() {
return {
item: null,
};
},
mounted() {
this.loadData();
},
computed: {
routeId() {
return this.$route.params.id;
},
},
watch: {
routeId() {
console.log("Reload (route change)");
this.loadData();
}, //reload when route id changes
},
methods: {
loadData() {
console.log("Reloading, ID", this.routeId);
if (!this.routeId) return; // no ID, leave early
routerid(this.$route.params.id).then((item) => {
this.item = item.data;
});
},
},
};
</script>
tabs.vue
import axios from "axios";
export const tabsandcontent = async (itemId) =>
await axios.get("https://fakestoreapi.com/products?limit=" + itemId);
<template>
<div>
<div v-if="item">
<h1>description: {{ item.description }}</h1>
</div>
</div>
</template>
<script>
import { tabsandcontent } from "./tabsandcontent";
export default {
name: "User",
components: {},
data() {
return {
item: null,
};
},
mounted() {
this.loadData();
},
computed: {
tabsandcontent() {
return this.$route.params.id;
},
},
watch: {
tabsandcontent() {
console.log("Reload (route change)");
this.loadData();
}, //reload when route id changes
},
methods: {
loadData() {
console.log("Reloading, ID", this.tabsandcontent);
if (!this.tabsandcontent) return; // no ID, leave early
tabsandcontent(this.$route.params.id).then((item) => {
this.item = item.data;
});
},
},
};
</script>
main.js
import Vue from "vue";
import App from "./App.vue";
import VueRouter from "vue-router";
import HelloWorld from "./components/HelloWorld";
import User from "./components/User";
Vue.use(VueRouter);
const router = new VueRouter({
routes: [
{
path: "/HelloWorld",
name: "HelloWorld",
component: HelloWorld,
children: [{ path: ":id", name: "User", component: User }]
}
]
});
Vue.config.productionTip = false;
new Vue({
router,
render: (h) => h(App)
}).$mount("#app");
code:- https://codesandbox.io/s/combined-logic-api-forked-41lh0f?file=/src/main.js
can you please answer this, In main.js routing I changed from path: "/" to path: "/HelloWorld" then all of sudden output not reflecting... because in my project path:'/' indicates login page??? In this scenario what changes, i need to make, to make logic work
also where is the relation between path:'/' and api call??
You have same name for the variables in tabs component (In watch and computed). And In tabsandcontent.js, you have missed to fetch description for the specific item as performed in routerId.js.
Have a look at modified version which is working as you expected.
https://codesandbox.io/embed/combined-logic-api-forked-ji5oh4?fontsize=14&hidenavigation=1&theme=dark
First thing first, I want you to know that I don't understand what are you asking for. But I'm going to try to answer.
Your first question:
In main.js routing I changed from path: "/" to path: "/HelloWorld" then all of sudden output not reflecting.
Yes, you will not see your HelloWorld.vue component. You can see your page however if you type <your-url>/HelloWorld. Usually the / path is used for something like "Home" page.
However, I've tried checking out your codesandbox. And take a look at your HelloWorld.vue component.
I think you are confused because when you changed the path from / to /HelloWorld apart from the HelloWorld.vue not showing up. It somehow broken the link which causes the API in tabs.vue not functioning.
If that's the case, you just have to simply add HelloWorld/${item.id} in tabs.vue,
<template>
<div>
<div v-for="item in items" :key="item.id">
<b> id: {{ item.id }}</b>
<router-link
:to="`HelloWorld/${item.id}`" // --> Notice this line
>
{{ item.title }}
</router-link>
</div><!-- end v-for -->
<router-view></router-view>
</div>
</template>
This however, isn't a common thing to do routing. You should add your App URLs to main.js. Which also isn't common, but I'm assuming this is just a little reproduction code you made for StackOverflow.
Here are my CodeSandbox edits.
https://codesandbox.io/s/combined-logic-api-forked-jttt8p
I will update the answer again later, I'm still not on my personal laptop.
I have a <form> in vue. I send that form to server, get a JSON response, print it to console. It works fine.
However I need to take that JSON response and display it on another page. For instance, I have two .vue files: GetAnimal.vue that has the form and retrieves the animal data from an API and a DisplayAnimal.vue that displays animal's data. I need to direct the response animal data from GetAnimal.vue to DisplayAnimal.vue.
GetAnimal.vue:
<template>
<form v-on:submit.prevent="getAnimal()">
<textarea v-model = "animal"
name = "animal" type="animal" id = "animal"
placeholder="Enter your animal here">
</textarea>
<button class = "custom-button dark-button"
type="submit">Get animal</button>
</form>
</template>
<script>
import axios from 'axios';
export default {
name: 'App',
data: function() {
return {
info: '',
animal: ''
}
},
methods: {
getAnimal: function() {
axios
.get('http://localhost:8088/animalsapi?animal=' + this.animal)
.then(response => (this.info = response.data));
console.log(this.info);
}
}
}
</script>
response:
retrieves a JSON with animal data, say like this:
{
"fur-color": "yellow",
"population": 51000,
"isExtinct": false,
"isDomesticated": true
}
and I now want to give that JSON to a DisplayAnimal.vue at /viewanimal endpoint:
DisplayAnimal.vue:
<template>
<div>
<p>Animal name: {{animal}}}</p>
<p>Fur color: {{furColor}}</p>
<p>Population: {{population}}</p>
<p>Is extinct: {{isExtinct}}</p>
<p>Is domesticated: {{isDomesticated}}</p>
</div>
</template>
How would I do that? I know I can redirect via this.$router.push({ path });, but I've only used it for navigation, while here JSON response needs to be passed. Is this even a correct / good practice way of approaching this?
EDIT:
I tried this:
in GetAnimal.vue I added this data:
data: function() {
return {
animal: {
name: 'Cat',
furColor: 'red',
population: '10000',
isExtinct: false,
isDomesticated: true
}
and in DisplayAnimal.vue this:
<script>
export default {
props: {
animal: {
name: {
type: String
},
furColor: {
type: String
},
population: String,
isExtinct: String,
isDomesticated: String
}
}
}
</script>
and in GetAnimal.vue I added this:
methods: {
animals: function() {
alert("animals");
this.$router.push({name: 'viewanimal',
query: {animal: JSON.stringify(this.animal)}});
},
to try to display that test animal using the display component. However it just didn't work - I get an empty page.
Using Vuex, you can solve this easily
Working example on netlify
https://m-animalfarm.netlify.app/
code on github
https://github.com/manojkmishra/animalfarm
GetAnimal.vue ( I have disabled axios call for testing and hardcoded info)
<template>
<form v-on:submit.prevent="getAnimal()">
<textarea v-model = "animal" name = "animal" type="animal" id = "animal"
placeholder="Enter your animal here">
</textarea>
<button class = "custom-button dark-button"
type="submit">Get animal</button>
</form>
</template>
<script>
import axios from 'axios';
export default
{
name: 'App',
data: function() { return { info: '', animal: '' } },
methods: {
getAnimal: function() {
// axios
// .get('http://localhost:8088/animalsapi?animal=' + this.animal)
// .then(response => (this.info = response.data),
this.info={"fur-color": "yellow","population": 51000,"isExtinct":
false,"isDomesticated": true },
this.$store.dispatch('storeAnimals', this.info)
//);
}
}
}
</script>
DisplayAnimal.vue
<template>
<div>
<p>Animal name: {{stateAnimal.animal}}</p>
<p>Fur color: {{stateAnimal.furColor}}</p>
<p>Population: {{stateAnimal.population}}</p>
<p>Is extinct: {{stateAnimal.isExtinct}}</p>
<p>Is domesticated: {{stateAnimal.isDomesticated}}</p>
</div>
</template>
<script>
import {mapState, mapGetters} from 'vuex';
export default {
computed:{ ...mapState({ stateAnimal:state => state.modulename.stateAnimal }),
},
}
</script>
modulename.js ( store module)
export default
{
state: {stateAnimal:null, },
getters:{ },
mutations:
{ ['STORE_ANIMALS'] (state, payload)
{ state.stateAnimal = payload;
console.log('state=',state)
},
},
actions:
{ storeAnimals: ({commit}, data) =>
{ console.log('storeanim-data-',data);
commit( 'STORE_ANIMALS', data );
},
}
}
Index.js (for vuex store), you can disable persistedstate as its for saving state if page is refreshed
import Vue from 'vue'
import Vuex from 'vuex'
import modulename from './modules/modulename'
import createPersistedState from "vuex-persistedstate";
Vue.use(Vuex)
export default new Vuex.Store({
plugins: [createPersistedState({ storage: sessionStorage })],
state: { },
mutations: { },
actions: { },
modules: { modulename }
})
State is available/shared for all the components
well first of all create a second folder call it services and create service.js for you axios call- good practice and cleaner code overall.
second use vuex. this kind of data is best used with vuex.
As far as I understand GetAnimal.vue is the parent component and you wish to display it in the child component DisplayAnimal.vue.
If so and you wish to see if this works just use props.
you can also send that same information or any other information for the child back to the parent using an $emit().
STRONGLY recommended to use vuex in order to manage the state
Vue.component('product',{
props:{
message:{
type:String,
required:true,
default:'Hi.'
}
},
template:`<div>{{message}}</div>`,
data(){...}
})
//html in the other component you axios call is in this component //<product meesage="hello"></product>
I would pass the animal name/id as a route param to the display page and have that component responsible for fetching and displaying the relevant animal data. This avoids the situation where a user could visit the display page directly via the URL and see an incomplete page.
In situations where you want to share local state between pages, as others have pointed out you'd probably want to use Vuex.
EDIT:
I'm adding some code to my answer as requested by the OP.
Routes:
const routes = [
{ path: "/", component: SearchAnimals },
{ path: "/viewanimal/:name", component: DisplayAnimal, name: "displayAnimal" }
];
DisplayAnimal.vue:
<template>
<div>
<p>Animal name: {{animal.name}}</p>
<p>Fur color: {{animal.furColor}}</p>
<p>Population: {{animal.population}}</p>
<p>Is extinct: {{animal.isExtinct}}</p>
<p>Is domesticated: {{animal.isDomesticated}}</p>
</div>
</template>
<script>
import axios from "axios";
export default {
name: "DisplayAnimal",
data: () => ({
animal: {}
}),
methods: {
fetchAnimal(name) {
axios
.get(`http://localhost:8088/animalsapi?animal=${name}`)
.then(response => {
this.animal = response.data;
});
}
},
created() {
this.fetchAnimal(this.$route.params.name);
}
};
</script>
SearchAnimals.vue:
<template>
<form v-on:submit.prevent="onSubmit">
<textarea
v-model="animal"
name="animal"
type="animal"
id="animal"
placeholder="Enter your animal here"
></textarea>
<button type="submit">Get animal</button>
</form>
</template>
<script>
export default {
name: "SearchAnimal",
data: () => ({
animal: ""
}),
methods: {
onSubmit() {
this.$router.push({
name: "displayAnimal",
params: { name: this.animal }
});
}
}
};
</script>
Obviously this is a bare-bones example, so doesn't contain any error handling etc., but it should get you up and running.
I'd like to render new component in vue.js as if it's new page.
I'm trying to do it with something called "dynamic component"
parent: Post.vue
child: Detail.vue
so, if one of the posts is clicked, Post is off and Detail is on.
The thing is I have to send clicked post's id to the Detail.
Here's some of my code.
Post.vue
<template>
<div>
<div v-if="loading">
loading...
</div>
<div v-else class="container">
<ul>
<li v-for="(post, index) in filteredPosts" v-bind:key="post.no">
<section class="post__main">
<div #click..?? class="main__title">{{post.title}}</div>
</section>
</li>
</ul>
</div>
</div>
</template>
<script>
created() {
axios.get(this.url, {
params: {
page: this.page,
ord: this.ord,
category: []
}
}).then(res => {
this.posts = res.data.list;
this.loading = false;
this.page++;
});
Detail.vue
<template>
<div>
{{contents}}
</div>
</template>
<script>
import axios from 'axios';
export default {
name: 'Datail',
data() {
return {
req_no: null,
contents: {}
}
},
created() {
axios.get(url, {
params: {
req_no: this.req_no
}
}).then(res => {
this.contents = this.res.data
});
}
}
</script>
I feel like I can do it with props and v-if.
Can someone help me? Thanks!
Once a post is clicked, pass post id to the click handler. In the click handler, route to detail.vue passing post id as route param.
like below:
<li v-for="(post, index) in filteredPosts" v-bind:key="post.no">
<section class="post__main">
<div #click="onPostClick(post.id)" class="main__title">{{post.title}}</div>
</section>
</li>
And in your click handler:
onPostClick(id: number) {
this.$router.push({
name: 'details',
params: {
id
}
});
}
This will work provided you set up vue router correctly in your app and have a valid route for details.
You can access the post id in details component as follows:
created() {
this.postId = this.$route.params.id;
}
I would take a look at the <component> which takes a prop :to and renders a component, this is good for something like tabs where you are rendering different component from a the same general location on the page without reloading the whole page. See here:
https://v2.vuejs.org/v2/guide/components-dynamic-async.html
This seems to be a very good use case for you, just pass into the component the props you need.
I have my firebase DB with this tree structure.
I use vue-fire to loop through the database. I want to retrieve the "CantFindMe" value in the name property. My files look like this:
// Dashboard.vue
<template>
<div class="">
<ul>
<li v-for="personName in names" v-bind:key="personName['.key']">
<div v-if="!personName.edit">
<p>{{ personName.forms.step1 }}</p>
</div>
</li>
</ul>
</div>
</template>
<script>
import { namesRef} from '../firebase.js'
export default {
name: 'app',
data () {
return {
name: ''
}
},
firebase: {
names: namesRef
}
}
</script>
// Firebase.js
{ The usual firebase config }
{...}
export const firebaseApp = firebase.initializeApp(config)
export const db = firebaseApp.database()
export const namesRef = db.ref('names')
I manage to read the object: { "-KxrySGBHgLvw_lPPdRA": { "edit": false, "name": "CantFindMe" } }
But when I try to add ".name" after ".step1", that should supposedly return "CantFindMe", I get nothing/blank back.
How do I get to the name property using VueJs to return "CantFindMe"?
Sorry for delay, Im not using vuefire... So, first - do not refer to names only, but directly to step1:
export const namesRef = db.ref('names/EcoClim/forms/step1/')
You will obtain structure like this:
[{
".key": "-KxrySGBHgLvw_lPPdRA",
"edit": "false",
"name": "CantFindMe"
}, {
...
}]
Now you can use it in template, but as key, refer to array index and not to FBase key:
<li v-for="(person, idx) in names" :key="idx">
<div v-if="!person.edit">
<p>{{ person.name }}</p>
</div>
</li>