Returning promise from api and putting into global data - vue.js

Sorry if this is obvious but new to vue and my js not so great.....
I'm trying to get some data from an api and then put that data into a global object so it can be passed down to some child components.
I can't seem to $set the data into the global projects object, it either returns undefined or returns a promise.
parent --
<template>
<div id="app">
<section class="intro">
<h1>WELCOME</h1>
</section>
<transition name="fade"> <router-view class="view" :computedProjects=computedProject ></router-view></transition>
</div>
</template>
<script>
import projectsList from './components/projectsList'
var client = contentful.createClient({
// This is the space ID. A space is like a project folder in Contentful terms
space: '',
// This is the access token for this space. Normally you get both ID and the token in the Contentful web app
accessToken: '',
})
var PRODUCT_CONTENT_TYPE_ID = 'project'
export default {
name: 'app',
components: {
projectsList
},
data: function() {
return {
users:'',
projects:'',
}
},
methods: {},
created : function () {
client.getEntries().then((response) => {
console.log(response)
this.projects = response
console.log(this.projects)
return response
});
},
computed: {
computedProject: function() {
if (!this.projects) return null;
return this.projects;
}
}
}
}
child ---
<template>
<section class="project-list">
<swiper :options="swiperOption">
<swiper-slide v-for="project in projects" v-bind:ref="'project' + project.name" :key="project.name" data-swiper-parallax="-100">
<div class="project-cover wrapper">
{{project.name}}
<router-link :to="{ name: 'project', params: { id: project.name }}">
<div class="cover-img" :style="{ 'background-image': 'url(../static/img/projects/'+project.name+'/'+'project-details-header.jpg)' }"> </div>
<div class="project-copy"></div>
<div href="#" class="tilter tilter--4">
<figure class="tilter__figure">
<img class="tilter__image" :src="'+project.fields.coverImage.fields.file.url+'" alt="" />
<div class="tilter__deco tilter__deco--shine"></div>
<div class="tilter__deco tilter__deco--overlay" v-bind:style="{ backgroundImage: project.gradient }"></div>
<figcaption class="tilter__caption">
<h1 class="projectName tilter__title title">{{project.name}}</h1>
<h2 class="tilter__description">{{project.tagline}}</h2>
</figcaption>
<svg class="tilter__deco tilter__deco--lines" viewBox="0 0 300 415">
<path d="M20.5,20.5h260v375h-260V20.5z" />
</svg>
</figure>
</div>
</router-link>
</div>
</swiper-slide>
<div class="swiper-pagination" slot="pagination"></div>
</swiper>
</section>
</template>
<script>
export default {
data: function () {
return {
swiperOption: {
autoplay:false,
setWrapperSize :true,
observeParents:true,
keyboardControl : true,
centeredSlides :true,
freeMode:false,
freeModeSticky:true,
pagination: '.swiper-pagination',
paginationType: 'progress',
watchSlidesProgress : true,
watchSlidesVisibility : true,
speed:650,
grabCursor:true,
parallax:true
},
}
},
props :['computedProject'],
created : function () {
console.log(this.projects)
},
any help will be appreciated.

You can just assign the vue data variable in the callback of getEntries like following:
created : function () {
var self = this
client.getEntries().then((response) => {
console.log(response)
self.projects = response
})
console.log(this.projects)
},

Related

VUE Props still undefined how to fix it?

productPage
<template>
<div id="products" class="products">
<div class="container">
<h1 class="text-center p-5">熱銷商品</h1>
<div class="row">
<div v-for="product in products" class="col-md-3">
<div class="card product-item">
<swiper :pagination="true" :modules="modules" class="mySwiper">
<swiper-slide v-for="image in product.data().images">
<img :src="image" class="card-img-top" alt="..." />
</swiper-slide>
</swiper>
<div class="card-body">
<div
class="d-flex flex-column justify-content-between text-center"
>
<h5 class="card-title">{{ product.data().name }}</h5>
<h5 class="card-priceS">
{{ currency(product.data().price) }}$
</h5>
<p>{{ product.data().description }}</p>
</div>
<add-to-cart
:name="product.data().name"
:price="product.data().price"
:product-id="product.id"
:product-image="getImage(product.data().images)"
>
</add-to-cart>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'Produtspage',
components: {
Navbar,
Login,
Swiper,
SwiperSlide,
},
props: {
msg: String,
},
setup() {
return {
modules: [Pagination],
}
},
data() {
return {
products: [],
}
},
mounted() {
this.readData()
},
methods: {
currency,
async readData() {
const querySnapshot = await getDocs(collection(db, 'products'))
querySnapshot.forEach((doc) => {
this.products.push(doc)
console.log(doc.id, ' => ', doc.data())
console.log(doc.data())
})
},
getImage(images) {
console.log(images)
return images
},
},
}
</script>
addtocart component
<template>
<div class="add-to-cart">
<button class="btn btn-success fs-6" #click="addToCart">
<i class="fa-solid fa-cart-plus mx-1"></i>加到購物車
</button>
</div>
</template>
<script>
export default {
name: 'AddToCart',
props: {
name: String,
price: String,
productId: String,
image: String,
},
data() {
return {
item: {
productName: this.name,
productPrice: this.price,
product_id: this.productId,
product_image: this.image,
// productQuantity: 1,
},
}
},
}
</script>
I checked the Vue Devtools, only image no props success, other data does have correct props, the image prop is still undefined, the image data is url and when I console.log(images), the data is shown, but props are still undefined.
I use firestore for the project, did I do anything wrong here?
you are using the wrong name for the binded attribute that dosnt match the prop name :
you are using :product-image in productPage
<add-to-cart
:name="product.data().name"
:price="product.data().price"
:product-id="product.id"
:product-image="getImage(product.data().images)"
>
so you have to match it with productImage instead of product-image as a prop name in the addtocart component
props: {
name: String,
price: String,
productId: String,
productImage: String, // appropriate prop name
},

Missing required prop in Vue.js

I am new to vue.js so maybe i'm missing something obvious. I have created 2 components Content.vue & ViewMore.vue. I am passing property "genre" which is inside array "animesByGenre" in Content.vue to ViewMore.vue but somehow it is not working.
Here is the Content.vue component:
<div v-for="(animesAndGenre, index) in animesByGenres" :key="index"
id="row1" class="container">
<h5>
{{animesAndGenre.genre.toUpperCase()}}
<button class="viewMore" v-bind:genre="animesAndGenre.genre"><router-link :to="{name: 'viewmore'}">view more</router-link></button>
</h5>
<vs-row vs-justify="center" class="row">
<vs-col v-for="(anime, i) in animesAndGenre.animes" :key="i"
vs-type="flex" vs-justify="center"
vs-align="center" vs-w="2" class="animeCard">
<vs-card actionable class="cardx">
<div slot="header" class="cardTitle">
<strong>
{{anime.attributes.canonicalTitle}}
</strong>
</div>
<div slot="media">
<img :src="anime.attributes.posterImage.medium">
</div>
<div>
<span>Rating: {{anime.attributes.averageRating}}</span>
</div>
<div slot="footer">
<vs-row vs-justify="center">
<vs-button #click="addAnimeToWatchlist(anime)"
color="primary" vs-type="gradient" >
Add to Watchlist
</vs-button>
</vs-row>
</div>
</vs-card>
</vs-col>
</vs-row>
</div>
<script>
import ViewMore from './ViewMore.vue';
import axios from 'axios'
export default {
name: 'Content',
components: {
'ViewMore': ViewMore,
},
data () {
return {
nextButton: false,
prevButton: false,
viewMoreButton: false,
results: '',
animes: '',
genres: ['adventure', 'action', 'thriller', 'mystery', 'horror'],
animesByGenres: []
}
},
created() {
this.getAnimes();
// this.getRowAnime();
this.genres.forEach( (genre) => {
this.getAnimeByGenres(genre);
});
},
}
</script>
Here is the ViewMore.vue component (i'm just trying to log genre for now):
<template>
<div>
</div>
</template>
<script>
import axios from 'axios'
export default {
props: {
genre: {
type: String,
required: true,
},
},
data() {
return {
allAnimes: '',
}
},
created() {
console.log(this.genre);
}
}
</script>
Passing props to routes doesn't work like that. Right now, all this code is doing is applying the genre prop to the button itself, not to the route it's going to. You'll need to add the genre to the URL as a param (/viewmore/:genre/), or as a part of the query (/viewmore?genre=...). See this page for how that works

Is it possible to sync Vuejs components displayed multiple times on the same page?

I have a web page that displays items. For each items there is a button (vuejs component) which allow user to toggle (add/remove) this item to his collection.
Here is the component:
<template lang="html">
<button type="button" #click="toggle" name="button" class="btn" :class="{'btn-danger': dAdded, 'btn-primary': !dAdded}">{{ dText }}</button>
</template>
<script>
export default {
props: {
added: Boolean,
text: String,
id: Number,
},
data() {
return {
dAdded: this.added,
dText: this.text,
dId: this.id
}
},
watch: {
added: function(newVal, oldVal) { // watch it
this.dAdded = this.added
},
text: function(newVal, oldVal) { // watch it
this.dText = this.text
},
id: function(newVal, oldVal) { // watch it
this.dId = this.id
}
},
methods: {
toggle: function(event) {
axios.post(route('frontend.user.profile.pop.toggle', {
pop_id: this.dId
}))
.then(response => {
this.dText = response.data.message
let success = response.data.success
this.dText = response.data.new_text
if (success) {
this.dAdded = success.attached.length
let cardPop = document.getElementById('card-pop-'+this.dId);
if(cardPop)
cardPop.classList.toggle('owned')
}
})
.catch(e => {
console.log(e)
})
}
}
}
</script>
For each item, the user can also open a modal, loaded by a click on this link:
<a href="#" data-toggle="modal" data-target="#popModal" #click="id = {{$pop->id}}">
<figure>
<img class="card-img-top" src="{{ URL::asset($pop->img_path) }}" alt="Card image cap">
</figure>
</a>
The modal is also a Vuejs component:
<template>
<section id="pop" class="h-100">
<div class="card">
<div class="container-fluid">
<div class="row">
<div class="col-12 col-lg-1 flex-column others d-none d-xl-block">
<div class="row flex-column h-100">
<div v-for="other_pop in pop.other_pops" class="col">
<a :href="route('frontend.pop.collection.detail', {collection: pop.collection.slug, pop: other_pop.slug})">
<img :src="other_pop.img_path" :alt="'{{ other_pop.name }}'" class="img-fluid">
</a>
</div>
<div class="col active order-3">
<img :src="pop.img_path" :alt="pop.name" class="img-fluid">
</div>
</div>
</div>
<div class="col-12 col-lg-6 content text-center">
<div class="row">
<div class="col-12">
<img :src="pop.img_path" :alt="pop.name" class="img-fluid">
</div>
<div class="col-6 text-right">
<toggle-pop :id="pop.id" :added="pop.in_user_collection" :text="pop.in_user_collection ? 'Supprimer' : 'Ajouter'"></toggle-pop>
</div>
<div class="col-6 text-left">
<!-- <btnaddpopwhishlist :pop_id="propid" :added="pop.in_user_whishlist" :text="pop.in_user_whishlist ? 'Supprimer' : 'Ajouter'"></btnaddpopwhishlist> -->
</div>
</div>
</div>
<div class="col-12 col-lg-5 infos">
<div class="header">
<h1 class="h-100">{{ pop.name }}</h1>
</div>
<div class="card yellow">
<div class="card p-0">
<div class="container-fluid">
<div class="row">
<div class="col-3 py-2">
</div>
<div class="col-6 py-2 bg-lightgray">
<h4>Collection:</h4>
<h3>{{ pop.collection ? pop.collection.name : '' }}</h3>
</div>
<div class="col-3 py-2 bg-lightgray text-center">
<a :href="route('frontend.index') + 'collections/' + pop.collection.slug" class="btn-round right white"></a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
</template>
<script>
export default {
props: {
id: Number
},
data() {
return {
pop: {
collection: {
}
}
}
},
ready: function() {
if (this.propid != -1)
this.fetchData()
},
watch: {
id: function(newVal, oldVal) { // watch it
// console.log('Prop changed: ', newVal, ' | was: ', oldVal)
this.fetchData()
}
},
computed: {
imgSrc: function() {
if (this.pop.img_path)
return 'storage/images/pops/' + this.pop.img_path
else
return ''
}
},
methods: {
fetchData() {
axios.get(route('frontend.api.v1.pops.show', this.id))
.then(response => {
// JSON responses are automatically parsed.
// console.log(response.data.data.collection)
this.pop = response.data.data
})
.catch(e => {
this.errors.push(e)
})
// console.log('fetchData')
}
}
}
</script>
Here is my app.js script :
window.Vue = require('vue');
Vue.component('pop-modal', require('./components/PopModal.vue'));
Vue.component('toggle-pop', require('./components/TogglePop.vue'));
const app = new Vue({
el: '#app',
props: {
id: Number
}
});
I would like to sync the states of the component named toggle-pop, how can I achieve this ? One is rendered by Blade template (laravel) and the other one by pop-modal component. But they are just the same, displayed at different places.
Thanks.
You could pass a state object as a property to the toggle-pop components. They could use this property to store/modify their state. In this way you can have multiple sets of components sharing state.
Your component could become:
<template lang="html">
<button type="button" #click="toggle" name="button" class="btn" :class="{'btn-danger': sstate.added, 'btn-primary': !sstate.added}">{{ sstate.text }}</button>
</template>
<script>
export default {
props: {
sstate: {
type: Object,
default: function() {
return { added: false, text: "", id: -1 };
}
}
},
data() {
return {};
},
methods: {
toggle: function(event) {
axios.post(route('frontend.user.profile.pop.toggle', {
pop_id: this.sstate.id
}))
.then(response => {
this.sstate.text = response.data.message
let success = response.data.success
this.sstate.text = response.data.new_text
if (success) {
this.sstate.ddded = success.attached.length
let cardPop = document.getElementById('card-pop-'+this.sstate.id);
if(cardPop)
cardPop.classList.toggle('owned')
}
})
.catch(e => {
console.log(e)
})
}
};
</script>
Live demo
https://codesandbox.io/s/vq8r33o1w7
If you are 100% sure that all toggle-pop components should always have the same state, you can choose to not define data as a function. Just declare it as an object.
data: {
dAdded: this.added,
dText: this.text,
dId: this.id
}
In https://v2.vuejs.org/v2/guide/components.html#data-Must-Be-a-Function, it mentions
a component’s data option must be a function, so that each instance
can maintain an independent copy of the returned data object
If Vue didn’t have this rule, clicking on one button would affect the
data of all other instances
Since you want to sync the data of all toggle-pop component instances, you don't have to follow the data option must be a function rule.

Vue.JS: turn every matching case to a hyperlink

I'm trying to turn every matching case from an array to a link
// I call the message component using this from another component
<ul>
<li
v-for="message in message"
v-bind:message="message"
is="message"
</ul>
// in the message component I have a mounted that reads the text message
// from the server and filters hashtags
data () {
return {
hashtagsInMessages: ''
};
},
mounted () {
const filterMessageHashtags = _.filter( this.message,text.split( ' ' ), value => hashtags.indexOf(value) != -1 );
this.hashtagsInMessages = filterMessageHashtags;
}
With this, how can I turn a message to a link, for instance:
hey how you doing #cool #fire #water bro?
To this, using Vue.js
hey how you doing #cool #fire #water bro?
This is my solution:
Vue.component("message-el", {
template: "#message",
props: ["message"],
created: function (){
this.messages = this.message.split(" ");
this.hashTags = this.messages.filter(function (message){
return message[0] === "#";
});
},
data: function (){
return {
messages: [],
hashTags: [],
};
},
methods: {
}
});
var app = new Vue({
el: "#app",
data: function (){
return {
}
},
methods: {
}
});
.none {
display: none;
}
<script src="https://unpkg.com/vue#2.5.2/dist/vue.js"></script>
<div id="app">
<message-el message="this is a message #tag1 #tag2">
</message-el>
</div>
<div id="message" class="none">
<div>
<div>
<template v-for="message in messages">
<a v-if="hashTags.indexOf(message) > -1" :href="'#' + message">
{{ message }}
</a>
<span v-else>
{{ message }}
</span>
&nbsp
</template>
</div>
</div>
</div>

Why is object not defined?

I want to try Vue.js 2 and started with a simple example. I've took this one from here https://jsfiddle.net/gmsa/gfg30Lgv/ and created a simple project with it. After I divided this code into files the project doesn't work. So I've made a data property a function:
data: function(){
return {
tabs: [{
name: "tab1",
id : 0,
isActive: true
}],
activeTab: {}
}
},
But there's an error in a console: Uncaught ReferenceError: newTab is not defined.
Project: https://github.com/rinatoptimus/vue-webpack-delete
File QueryBrowserContainer:
<template>
<div id="queryBrowserContainer">
<p>queryBrowserContainer text</p>
<ul class="nav nav-tabs" role="tablist">
<li role="presentation" v-for="tab in tabs" :class="{active:tab.isActive}">
{{ tab.name }}
</li>
<li>
<button type="button" class="btn btn-primary" #click="openNewTab">New tab</button>
</li>
</ul>
<div class="tab-content">
<div v-for="tab in tabs" role="tabpanel" class="tab-pane" :class="{active:tab.isActive}">
<app-querybrowsertab :tab="tab"></app-querybrowsertab>
</div>
</div>
<pre>{{ $data | json }}</pre>
</div>
</template>
<script>
import QueryBrowserTab from './QueryBrowserTab.vue';
export default {
data: function(){
return {
tabs: [{
name: "tab1",
id : 0,
isActive: true
}],
activeTab: {}
}
},
ready: function () {
this.setActive(this.tabs[0]);
},
methods: {
setActive: function (tab) {
var self = this;
tab.isActive = true;
this.activeTab = tab;
/*this.activeTab.isActive = true;
console.log("activeTab name=" + this.activeTab.name);*/
this.tabs.forEach(function (tab) {
if (tab.id !== self.activeTab.id) { tab.isActive = false;}
});
},
openNewTab: function () {
newTab = {
name: "tab" + (this.tabs.length + 1),
id: this.tabs.length,
isActive: true
};
this.tabs.push(newTab);
this.setActive(newTab);
/*this.activeTab = newTab;
console.log("### newtab name=" + newTab.name);*/
},
test: function() {
alert('676767');
},
closeTab: function () {
console.log("### CLOSE!");
}
}
}
File QueryBrowserTab:
<template>
<div>
<p>querybbbTab</p>
<h3>{{tab.name}}</h3>
<h3>{{tab.id}}</h3>
</div>
</template>
<script>
import QueryBrowserContainer from './QueryBrowserContainer.vue';
export default {
data: function () {
return {
databaseOptions: [],
};
},
props: ['tab'],
methods: {},
components: {
'app-querybrowsercontainer': QueryBrowserContainer
}
}
</script>
File App:
<template>
<div id="app">
<app-message></app-message>
<app-querybrowsertab></app-querybrowsertab>
<app-querybrowsercontainer></app-querybrowsercontainer>
</div>
</template>
<script>
export default {
name: 'app',
data () {
return {}
}
}
</script>
It seems in file: QueryBrowserTab, you have not passed tab props, but you are using it, make sure you pass tab as props from whatever places you are using it.
As stated in the docs here, you can pass props to a component like following:
<app-querybrowsertab :tab="tab"></app-querybrowsertab>
which you are already doing in app-querybrowsercontainer,but in file App, you are not passing the prop, which might be the source of error for you.