Get data from an Array in Vue - javascript

Right now, displayed data in my page is an array as per snippet below:
But I want only to get or display the project names.
This is what I have only right now:
fetch(context,id){
if(context.getters['isEmpty']){
Vue.api.mediasetting.index()
.then(response=>{
var medias = response.data[key];
context.commit('setMedias',medias);
// console.log("init medias", medias[0].project_name)
},response=>{
});
}
},
Can I apply filter here and how? Thanks.

Depends on what html element you want to use for rendering the values, you may use a v-for directive to rendering the project_name values:
<template>
<ul id="example">
<li v-for="(media) in medias">
{{ media.project_name }}
</li>
</ul>
</template>
You are going to need to get the medias data from your store, you may get it using a computed property:
<script>
export default {
computed: {
medias: function () {
return this.$store.medias
}
}
}
</script>
Example above is assuming you are using single file component structure.

Related

Checking for multiple route paths in vue v-bind

I am currently adding a class to a li element based on the route.path using the following code;
<li v-bind:class="{ 'current': $route.path == '/services' }"><nuxt-link to="/services">Services</nuxt-link>
I want to check for multiple paths but i am unsure if that is possible.
Any help would be great.
It's possible to check for mulitple paths by using an array of possible values, and then Array.prototype.includes to check for existence:
<li v-bind:class="{ 'current': ['/services', '/contact', '/about'].includes($route.path) }">
To improve template readability, consider moving that to a computed prop:
<template>
<li v-bind:class="myClasses">
...
</template>
<script>
export default {
computed: {
myClasses() {
return {
const possiblePaths = ['/services', '/contact', '/about']
current: possiblePaths.includes(this.$route.path)
}
}
}
}
</script>

Vue.js warns You may have an infinite update loop in a component render function

I'm quite new to Vue.js & Buefy and I believe I understand why this happens but have no idea how to fix it.
I have a list of project partners sorted by country and I need to output a list with checkboxes (I'm using Buefy on the page anyway) and country names as title (only when there's a "new" country). This ends with browser doing infinite loop (verified with console.log) and Vue Devtools gives warning "You may have an infinite update loop in a component render function".
I believe this happens because changing prevTitle triggers re-rendering. I know it's not possible to pass parameters to computed properties and I haven't been able to use any of the tricks I've found to make partner.country available there.
var app = new Vue({
el: "#app",
data: {
prevTitle: ""
...
methods: {
changeCountryTitle(country) {
if (country != this.prevTitle) {
this.prevTitle = country;
return true;
}
return false;
}
<template v-for="partner in results">
<div v-if="changeCountryTitle(partner.country)">
{{ partner.country }}
</div>
<b-checkbox ... >{{ partner.name }}, ...</b-checkbox>
<br />
</template>
Then I tried to make a computed property where I do all processing instead of for loop in template and return a string that contains everything, including Buefy tags, which would be called
<span v-html="printPartnerList"></span>
But those Buefy tags don't get rendered properly, only HTML tags work, browser ignores Buefy tags showing the normal text only.
Any ideas how get this working? For time being I print country names for each partner after name etc. but it's not how this is supposed to work.
v-html doesn't evaluate Vue (or in this case Buefy) components, only regular HTML tags.
Your first approach wasn't that bad, but instead of calling a method inside the v-for you can add a computed property in the items that tells if the country should be rendered:
<template v-for="partner in computedResults">
<div v-if="partner.showCountry">
{{ partner.country }}
</div>
<b-checkbox ... >{{ partner.name }}, ...</b-checkbox>
<br />
</template>
New app:
var app = new Vue({
el: "#app",
data: {
// prevTitle: "" <- not needed
...
computed: {
computedResults() {
let prevCountry = ''
let newResults = []
this.results.forEach(partner => {
let showCountry = false
if (prevCountry != partner.country) {
showCountry = true
}
newResults.push({
...partner,
showCountry
})
prevCountry = partner.country
})
return newResults
}

Using a method to get array to iterate through with a V-for loop, but nothing is being displayed

I am making a personal project to learn how Vue.js and Express communicate together. I managed to get the desired data from the server by passing a prop in the Vue template (brand of watch to be fetched from the server) as an argument to a method that fetches the data.
This method is being called in the V-for declaration and returns an array of objects. Now, when I test this with a standard array that I instantiated inside the data function all is well, and the data displays just fine. The method definitely fetches the data because I can see it inside Vue devtools and when I print it to the console. So it is there, but for some reason it doesn't want to play nice with V-for.
Here's the code:
<template>
<div class="wrapper">
<div class="product" v-for="(product, index) in getProductsByBrand(brand)" :key="index">
<div class="product-name">
{{ product }}
</div>
</div>
</div>
</template>
<script>
import ProductService from '#/services/ProductService'
export default {
name: 'product',
props: [
'brand'
],
data () {
return {
products: [],
shoppingItems: [
{ name: 'apple', price: '7' },
{ name: 'orange', price: '12' }
]
}
},
methods: {
async getProductsByBrand (brand) {
const response = await ProductService.fetchProductsByBrand(brand)
this.products = response.data.product
console.log(this.products)
console.log(this.shoppingItems)
return this.products
}
When I replace the getProductsByBrand() method with the shoppingItems array it all works. That method returns another array, so I don't see why V-for is having trouble with displaying that data.
Any help is much appreciated!
Vue templates are rendered by a render function, and it's a normal sync function. Therefore you can't call an async function in the template. A better pattern for your code would be as follows:
Template:
<div class="product" v-for="(product, index) in products" :key="index">
Script:
methods: {
async getProductsByBrand (brand) {
const response = await ProductService.fetchProductsByBrand(brand)
return response.data.product
}
},
async created() {
this.products = await this.getProductsByBrand(this.brand);
}
It amounts to the same thing but loads the data into the products data property during the created lifecycle hook.

Linking a text field in a child component to a parent component's props in VueJS

I have a child component sending data via an event to a parent component in VueJS. From the parent component, I am routing the data (or trying to route the data...) to a sibling of the child and create new components with the data sent from the child.
I use a dictionary to group the data for various reasons, then push the dictionary into an array. A v-for loop loops thru the array and populates the previously mentioned new components with data found in that array. I probably don't need to do it this way, but that's how I'm doing it. I am open to alternatives.
Anyway, it doesn't work great. So far I'm only able to get one of the three strings I need to show up where I want it to. I'll explain more after I post the code.
Already tried:
A dozen different versions of the code, including creating a simple v-for in a list to do the job, and various versions with/without a dictionary or array.
In my research for the problem I've gone through the VueJS docs, Googled a few things, and found nothing.
In App.vue (I tried to remove all the irrelevant stuff):
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png">
<TweetDeck v-on:messageFromTweetDeck="msgReceived($event)"/>
<!-- <ul>
<li v-for="(tweet, index) in tweets" :key="index">{{ tweet }}</li>
</ul>-->
<TwitterMsg v-for="(tweet, index) in tweets" :key="index"
:name="tweet.name" :handle="tweet.handle" tsp=3 :msg="tweet.tweet" />
<TwitterMsg name="aaa" handle='aaa'
tsp=50 msg="hey this is a message on twitter"/>
<input type="text" v-model="placeholderText"/>
</div>
</template>
<script>
import TwitterMsg from './components/TwitterMsg.vue'
import TweetDeck from './components/TweetDeck.vue'
export default {
name: 'app',
components: {
TwitterMsg,
TweetDeck
},
data: function() {
return {
tweets: [],
message: "",
placeholderText: ""
}
},
methods: {
msgReceived(theTweet, name, handle) {
this.tweets.push({tweet: theTweet, name: name, handle: handle})
}
}
}
</script>
And in TweetDeck.vue:
<template>
<div>
<input type='text' v-model="yourName">
<input type='text' v-model="yourHandle">
<input type='text' v-model="yourTweet"/>
<button type='button' #click="sendTweet()">Tweet</button>
</div>
</template>
<script>
export default {
name: "TweetDeck",
data: function() {
return {
yourName: "Your name here",
yourHandle: "Your twitter handle",
yourTweet: "What's going on?"
}
},
methods: {
sendTweet() {
this.$emit('messageFromTweetDeck', this.yourTweet, this.yourName, this.yourHandle);
}
}
}
</script>
You can also see the mostly unimportant TwitterMsg.vue here (I am trying to copy Twitter for learning purposes:
<template>
<div>
<h4>{{ name }}</h4>
<span>#{{ handle }}</span>
<span> {{ tsp }}</span> <!-- Time Since Posting = tsp -->
<span>{{ msg }}</span>
<img src='../assets/twit_reply.png'/><span>1</span>
<img src="../assets/twit_retweet.png"/><span>2</span>
<img src="../assets/twit_fave.png"/><span>3</span>
</div>
</template>
<script>
export default {
name: "TwitterMsg",
props: {
name: String,
handle: String,
tsp: String,
msg: String
}
}
</script>
<style>
img {
width: 30px;
height: 30px;
}
</style>
Expected result:
The code populates a new TwitterMsg component with appropriate name, handle and message data each time I click the "Tweet" button.
Actual results:
My code fails to help the name and handle strings make it from the input text box in TweetDeck.vue all the way to their home in TwitterMsg.vue.
I will say that this.yourTweet in TweetDeck.vue DOES manage to make it all the way to its destination, which is good -- though it makes me wonder why the other two pieces of data didn't follow suite.
Totally lost. Also just in my first month of VueJS so it's pretty good that I can even make one string appear where I want it to. \o/
First, you need to remove the $event parameter
<TweetDeck v-on:messageFromTweetDeck="msgReceived"/>
Second, you can optimize the data format passed to the parent component:
sendTweet() {
this.$emit("messageFromTweetDeck",
{ tweet: this.yourTweet, name: this.yourName, handle: this.yourHandle }
);
}
And then modify your msgReceived method:
msgReceived(childData) {
this.tweets.push(childData);
}
Link: codesandbox
Hope to help you:)

VueJS does not rerender list even though array has changed & v-key is specified

I have two entities: categories and subcategories and two lists. When user selects a category I display subcategory list.
At first try it works fine, but when I change category I got old subcategory list.
Both categories and subcategories are in vuex store.
I tried to calculated subcategories next ways:
Computed property
When user selects a category I invoke a method, which filters subcategories by category_id and returns new list
Nothing worked for me. Here is an example of computed property:
subCategories() {
return this.getClothingSubCategories.filter((subCategory) => {
return subCategory.clothing_category_id === this.category.clothing_category_id
})
},
Where this.getClothingSubCategories is a vuex getter.
What's weird is that both in vue plugin (in chrome developer tools) and console I got the list updated, but in html the list is old.
Here is how I display the list:
<VuePerfectScrollbar class="categories"
v-if="showSubCategories"
v-once>
<ul>
<li v-for="subCategory in subCategories"
:key="subCategory.clothing_subcategory_id"
#click="selectSubCategory(subCategory)">
<div class="title">{{ subCategory.title }}</div>
<div class="arrow">></div>
</li>
</ul>
</VuePerfectScrollbar>
So, the subCategories property relies on category object, which I set simply:
selectCategory(category) {
this.category = category
},
When user selects a category.
I specified :key, tried different approaches but nothing worked, I always get the old list of subCategories.
What could be the reason?
update
Vuex getter:
export const getClothingSubCategories = (state) => {
return state.clothingSubCategories
}
Component data:
data() {
return {
gender: 'female',
category: null,
subCategory: null,
good: null,
filters: {
gender: 'female',
clothing_subcategory_id: null
}
}
},
Consider your declaration:
<VuePerfectScrollbar class="categories"
v-if="showSubCategories"
v-once>
<ul>
<li v-for="subCategory in subCategories"
:key="subCategory.clothing_subcategory_id"
#click="selectSubCategory(subCategory)">
<div class="title">{{ subCategory.title }}</div>
<div class="arrow">></div>
</li>
</ul>
</VuePerfectScrollbar>
The subCategories value, which is a computed property:
subCategories() {
return this.getClothingSubCategories.filter((subCategory) => {
return subCategory.clothing_category_id === this.category.clothing_category_id
})
},
is supposed to be updated whenever category changes.
As you reported, it does. As it should.
Your problem, though is:
v-once
Details:
Render the element and component once only. On subsequent re-renders,
the element/component and all its children will be treated as static
content and skipped. This can be used to optimize update performance.
That is, as soon as Vue renders the subcategory collection the first time it "freezes" it. Basically, because of v-once it will never be updated again.
Solution: remove v-once.
I suggest you remove from other places in your code as well. Any element you want to be updated should not have v-once.

Categories

Resources