Iterating over keys instead of values Vue app - javascript

I'm trying to select the values for three keys from my api with axios.
Each time I make a request I'm displaying the values four times for each key. I dropped a key from my data models and I re tested. When I did that my request displayed three times instead of four. Meaning I'm iterating over the keys instead of the values.
How Can I change this code to display only the values of each key? I am using nuxt django and axios.
This is the json data axios returns.
{"id":1,"balance":10.0,"exposure":7.0,"free_funds":80.0}
How do I just get the values only for each paragraph tag instead of the values for each paragraph tag 4 times?
<template>
<div class="container">
<h2>Current Balance</h2>
<ul class="trendings">
<li v-for="result in results" :key="result">
<p>{{ results.balance }} balance</p>
<p>{{ results.free_funds }} free_funds</p>
<p>{{ results.exposure }} exposure</p>
</li>
</ul>
</div>
</template>
<script>
import axios from "axios";
export default {
asyncData() {
return axios.get("http://localhost:8000/api/balance/1").then(res => {
return { results: res.data };
});
}
};
</script>

If the JSON response looks exactly like this:
{"id":1,"balance":10.0,"exposure":7.0,"free_funds":80.0}
Then you don't even need the v-for because there is only one thing to iterate over.
If you want a v-for, force the data to come back as an array by wrapping it so that it would be this:
[ {"id":1,"balance":10.0,"exposure":7.0,"free_funds":80.0} ]
So:
return { results: [res.data] };

See the docs
<li v-for="(result, key) in results" :key="result">
<p>{{ result }} {{ key }}</p>
</li>

Related

Render images from Entries in Contenful + Angular app

The issues:
How to I access the includes array so I can get the image url to render the image? (or how to I get the images to render in general)
Why is are my description fields not rendering?
So I have a contentful service that gets all the entries as such:
//get all exhibits
getExhibits(query?: object): Promise<Entry<any>[]> {
return this.cdaClient.getEntries(Object.assign({
include : 2,
content_type: CONFIG.contentTypeIds.exhibit
}, query))
.then(res => res.items);
}
The above function is called in my Gallery component as such:
ngOnInit() {
this.contentfulService.getExhibits()
.then(exhibits => this.exhibits = exhibits);
}
And the data is rendered in the corresponding HTML as such:
<section class="gallery__section" [class.menu--open]="menu.isMenuClosed" *ngFor="let exhibit of exhibits">
<figure>
<img src="{{ exhibit.fields.file.url }}" alt="">
<!-- HOW DO I GET THE IMG URL -->
</figure>
<div class="gallery__text">
<h2 class="gallery__heading">Gallery</h2>
<p class="gallery__title">{{ exhibit.fields.title }}</p>
<h3 class="gallery__artist">{{ exhibit.fields.artist }}</h3>
<p class="gallery_onview">On View</p>
<p class="gallery__dates">{{ exhibit.fields.date }}</p>
<p class="gallery__description" *ngIf="exhibit.fields.description.content[0].content[0].value">{{ exhibit.fields.description.content[0].content[0].value }}</p>
</div>
</section>
Bellow is the JSON of one item the items array:
And here the image that is supposed be part of the above item, but is placed in a different array called includes:
Below is the console.log output of one exhibit:
You don't need to access the includes array. The Contentful SDK will automatically resolve the fields that contain a reference.
For your image issue, I think you're missing the field name for your file field: exhibit.fields.YOUR_FIELD_NAME.fields.file.url
For your description field, I would recommend using ngx-contentful-rich-text to parse the rich text into HTML.

How To Make Filtered List More Dynamic In Vue

So I have a list of items that I am looping through using v-for. Before the list is printed it is filtered by a value using the .filter() method. Also to change between filtered values I am using v-on:click. My question is, is there a way I could make the filter more dynamic? because if the predefined values I have set to filter the list are changed then the filter will no longer work...
I have provided a JSFiddle here So you could see what it is that I am working with.
My 2 main areas of concern are the v-on and computed methods..
<li class="d-flex justify-content-between" v-on:click="engagementFilterKey = 'recieved'">
<div class="ml-3">
<span class="text-muted">Received</span>
</div>
</li>
<li class="d-flex justify-content-between" v-on:click="engagementFilterKey = 'preparation'">
<div class="ml-3">
<span class="text-muted">Preparation</span>
</div>
</li>
Here is the computed methods
computed: {
engagementFilter() {
return this[this.engagementFilterKey];
},
recieved() {
return this.allEngagements.filter((engagement) => engagement.status == 'Recieved')
},
preparation() {
return this.allEngagements.filter((engagement) => engagement.status == 'Preparation')
},
Please view the fiddle I posted at the top of this discussion...
Updated
Idea is to create unique set of statuses from an array of engagements and dynamically render list of buttons. Each button will assign engagementFilterKey which can be used to filter out matching statuses from the list of all engagements.
Solution code - JSFiddle

How to define a temporary variable in Vue.js template

Here is my current template:
<a-droppable v-for="n in curSize" :key="n - 1" :style="{width: `${99.99 / rowLenMap[orderList[n - 1]]}%`, order: orderList[n - 1]}">
<a-draggable :class="{thin: rowLenMap[orderList[n - 1]] > 10}">
<some-inner-element>{{rowLenMap[orderList[n - 1]]}}</some-inner-element>
</a-draggable>
</a-droppable>
The problem is that i have to write rowLenMap[orderList[n - 1]] multiple times, and i'm afraid vue.js engine will also calculate it multiple times.
What i want is something like this:
<a-droppable v-for="n in curSize" :key="n - 1" v-define="rowLenMap[orderList[n - 1]] as rowLen" :style="{width: `${99.99 / rowLen}%`, order: orderList[n - 1]}">
<a-draggable :class="{thin: rowLen > 10}">
<some-inner-element>{{rowLen}}</some-inner-element>
</a-draggable>
</a-droppable>
I think it's not difficult to implement technically because it can be clumsily solved by using something like v-for="rowLen in [rowLenMap[orderList[n - 1]]]". So is there any concise and official solution?
I found a very simple (almost magical) way to achieve that,
All it does is define an inline (local) variable with the value you want to use multiple times:
<li v-for="id in users" :key="id" :set="user = getUser(id)">
<img :src="user.avatar" />
{{ user.name }}
{{ user.homepage }}
</li>
Note : set is not a special prop in Vuejs, it's just used as a placeholder for our variable definition.
Source: https://dev.to/pbastowski/comment/7fc9
CodePen: https://codepen.io/mmghv/pen/dBqGjM
Update : Based on comments from #vir us
This doesn't work with events, for example #click="showUser(user)" will not pass the correct user, rather it will always be the last evaluated user, that's because the user temp variable will get re-used and replaced on every circle of the loop.
So this solution is only perfect for template rendering because if component needs re-render, it will re-evaluate the variable again.
But if you really need to use it with events (although not advisable), you need to define an outer array to hold multiple variables at the same time :
<ul :set="tmpUsers = []">
<li v-for="(id, i) in users" :key="id" :set="tmpUsers[i] = getUser(id)" #click="showUser(tmpUsers[i])">
<img :src="tmpUsers[i].avatar" />
{{ tmpUsers[i].name }}
{{ tmpUsers[i].homepage }}
</li>
</ul>
https://codepen.io/mmghv/pen/zYvbPKv
credits : #vir us
Although it doesn't make sense here to basically duplicate the users array, this could be handy in other situations where you need to call expensive functions to get the data, but I would argue you're better off using computed property to build the array then.
Judging by your template, you're probably best off with a computed property, as suggested in the accepted answer.
However, since the question title is a bit broader (and comes up pretty high on Google for "variables in Vue templates"), I'll try to provide a more generic answer.
Especially if you don't need every item of an array transformed, a computed property can be kind of a waste. A child component may also be overkill, in particular if it's really small (which would make it 20% template, 20% logic and 60% props definition boilerplate).
A pretty straightforward approach I like to use is a small helper component (let's call it <Pass>):
const Pass = {
render() {
return this.$scopedSlots.default(this.$attrs)
}
}
Now we can write your component like this:
<Pass v-for="n in curSize" :key="n - 1" :rowLen="rowLenMap[orderList[n - 1]]" v-slot="{ rowLen }">
<a-droppable :style="{width: `${99.99 / rowLen}%`, order: orderList[n - 1]}">
<a-draggable :class="{thin: rowLen > 10}">
<some-inner-element>{{rowLen}}</some-inner-element>
</a-draggable>
</a-droppable>
</Pass>
<Pass> works by creating a scoped slot. Read more about scoped slots on the Vue.js documentation or about the approach above in the dev.to article I wrote on the topic.
Appendix: Vue 3
Vue 3 has a slightly different approach to slots. First, the <Pass> component source code needs to be adjusted like this:
const Pass = {
render() {
return this.$slots.default(this.$attrs)
}
}
Today I needed this and used <template> tag and v-for like this
I took this code and
<ul>
<li v-for="key in keys"
v-if="complexComputation(key) && complexComputation(key).isAuthorized">
{{complexComputation(key).name}}
</li>
</ul>
Changed it to this
<ul>
<template v-for="key in keys">
<li v-for="complexObject in [complexComputation(key)]"
v-if="complexObject && complexObject.isAuthorized">
{{complexObject.name}}
</li>
</template>
</ul>
And it worked and I was pleasantly surprised because I didn't know this was possible
This seems like the perfect use case of a child component. You can simply pass your complex computed value(s) as a property to the component.
https://v2.vuejs.org/v2/guide/components.html#Passing-Data-to-Child-Components-with-Props
How about this:
<div id="app">
<div
v-for="( id, index, user=getUser(id) ) in users"
:key="id"
>
{{ user.name }}, {{ user.age }} years old
<span #click="show(user)">| Click to Show {{user.name}} |</span>
</div>
</div>
CodePen: https://codepen.io/Vladimir-Miloevi/pen/xxJZKKx
<template>
<div>
<div v-for="item in itemsList" :key="item.id">
{{ item.name }}
<input v-model="item.description" type="text" />
<button type="button" #click="exampleClick(item.id, item.description)">
Click
</button>
</div>
</div>
</template>
<script>
export default {
data() {
return {
items: [
{
id: 1,
name: 'Name1',
},
{
id: 2,
name: 'Name2',
},
],
}
},
computed: {
itemsList() {
return this.items.map((item) => {
return Object.assign(item, { description: '' })
})
},
},
methods: {
exampleClick(id, description) {
alert(JSON.stringify({ id, description }))
},
},
}
</script>
Just tested using vue3 and works, i think it works universally
{{ (somevariable = 'asdf', null) }}
<span v-if="somevariable=='asdf'">Yey</span>
<span v-else>Ney</span>
It outputs nothing while setting your variable.
mandatory:
opening "("
set your variable
closing ", null)"
curSize is an array. Your temporary values comprise a corresponding implied array sizedOrderList = curSize.map(n => orderList[n-1]). If you define that as a computed, your HTML becomes
<a-droppable v-for="n, index in sizedOrderList" :key="curSize[index]" :style="{width: `${99.99 / rowLenMap[n]}%`, order: n}">
<a-draggable :class="{thin: rowLenMap[n] > 10}">
<some-inner-element>{{rowLenMap[n]}}</some-inner-element>
</a-draggable>
</a-droppable>

How do I pass the current item in a for loop to a method in vue.js 2?

I am building (as an exercise) a shopping cart in vue.js 2. I have my shop items and order items stored in my vue data array and a button rendered in a for loop for each shop item to add the item to the order (ex. push).
Here is the section of code that houses my list of items from my shop array in my vue data:
<fieldset>
<legend>Shop</legend>
<section v-if="shop">
<article v-for="(item, index) in shop">
<header><h1>{{ item.title }}</h1></header>
<p>{{ item.description }}</p>
<footer>
<ul>
<li>${{item.price}}</li>
<!-- find a way to set input name -->
<li><label>Quantity <input type="number" name=""></label></li>
<li><button v-on:click="addItemToOrder($event)">Add to Order</button></li>
</ul>
</footer>
</article>
</section>
<p v-else>No Items to Display</p>
</fieldset>
here is my vue element:
new Vue({
el: '#my-order',
data:{
'shop':[
{
'title':'Website Content',
'description':"Order your Website content by the page. Our search-engine-optimized web content puts you ahead of the competition. 250 words.",
'price':25,
'sku':'web001'
},
{
'title':'Blog Post',
'description':"We write blog posts that position your website as a go-to resource for expert knowlegde.",
'price':50,
'sku':'blog001'
},
{
'title':'Twitter Post'
},
{
'title':'Product Description'
}
],
'customizations':null,
'order':{
'items':null,
'total':null
},
'customer':null,
'payment':null
},
methods:{
addItemToOrder: function(){
/* Here is where I need to append the item to the cart */
}
}
})
How do I pass the item in the for loop to the order (eg: append it to order.items)?
You just need to pass the item in as a parameter to the function.
v-on:click="addItemToOrder(item)"
Then you can use it your Vue component
addItemToOrder: function(item){
this.order.items.push(item);
}
Make sure you initialize order.items to an empty array inside your components data so that you can push to it.
'order':{
'items': [],
'total': 0
},
In general, it is a good idea to initialize your data to the correct data type if you know what it will be.
I realise this is a bit late however in case anyone else happens across this thread...
You need to pass in the event as well as the item
in your vue code
someMethod : function(e, item){}
in your html
<a v-on:click="someMethod($event, $data)"></a>

Angular factory filter - Can't get data passed to the filter

I'm looking through the Angular docs and I can't better documented stuff.
My problem is as follows:
I have a filter:
.filter('filteringService', function () {
return function (photos, categsList) {
if (photos !== undefined) {
var filteredPosts = [];
console.log('Categories or Selected Categories:');
console.log(categsList);
//DEVEL Only, assign a SEARCH value, can't pass the data from the list now.
categsList = 'people';
if (categsList === undefined) {
for(i=0; i < photos.length; i++) {
filteredPosts.push(photos[i]);
}
} else {
console.log('Trying to push a category slug to search for.');
//TASK: Convert in multiple possible selections.
filteredPosts = [];
}
console.log('Filter returns ' + filteredPosts.length + ' posts');
return filteredPosts;
}
};
});
And I have the template
<div class="photos">
<div class="filters">
<ul>
<li><a>ALL</a></li>
<li ng-repeat="category in categsList">
<a ng-checked="category[0]" ng-model="category[0]">{{ category[1] }}</a>
</li>
</ul>
</div>
<ul class="photos-list">
<li ng-repeat="photo in photos|filteringService:category">
<h1>{{ photo.title }} click LINK</h1>
<ul class="categories">
<li ng-repeat="category in photo.categories">
{{ category.title }}
</li>
</ul>
</li>
</ul>
</div>
There's a huge object with posts called photos and then there's a category list called categsList.
The photos object has the items from the categs list in it. I WANT to be able to filter with the CLICKED element through that list, and maybe multiple filter, but first to pass on the actual filter value to the filter service, I can't seem to do that.
How should I do that?
Apparently I managed to pass the filter value, in a dirty way (I GUESS), like this
<a ng-bind="category.slug" ng-click="returnFilter(category.slug);" ng-model="category.slug">{{ category.title }}</a>
it goes here
$scope.returnFilter = function(theSlug) {
$scope.filterBy = theSlug;
};
and it comes out here
<li ng-repeat="photo in photos|filteringService:filterBy">
It's working, but Is this correct?
EDIT: Also with this way in mind, I could pass an array as theSlug so I can do multiple filtering, and when clicking two times on the same item it would get it out of the array. hmmm
EDIT 2:
Let's say the resulting array is under 15 items, could do I run some action in the controller knowing this?
Actually the other way around, could I query from the controller the resulting array that the filter outputs?
I can't $watch the first array, I guess the filter creates a new array and puts those results in page. How could I watch the resulting array for changes and do stuff in the controller?

Categories