Vue filter in v-for - vuejs2

Hi I am pretty new to VueJS and have started working on a very simple API request. I have an object that looks like this:
posts: [
text: "String",
choices: {"1":"Yes","2":"No"}
]
Coming from angular, this seems very straightforward. I would just use a filter to convert choices to an object and loop over it. However, I ran into a problem. When I attempt to use the filter 'log' or 'json' in the v-for, they don't work.
<template>
<div>
<li v-for="(post,index) in posts | log">
<ul>
{{ post.text | log }}
{{ post.choices | json }}
<li v-for="(value,key) in post.choices | json">
{{value}} {{key}}
</li>
</ul>
</li>
</div>
</template>
<script>
import {HTTP} from './main';
export default {
filters: {
json: function (value) {
return JSON.parse(value)
},
log: function (value) {
console.log(value)
}
},
props: [
'apiKey'
],
data: () => ({
posts: [],
post: [],
errors: []
}),
created() {
HTTP.get('questions', { headers: { 'Api-Key': this.apiKey} })
.then(response => {
this.posts = response.data
})
.catch(e => {
this.errors.push(e)
})
}
}
</script>
Then no data shows up, however they work fine in the mustache template. Any thoughts on how I can accomplish this?
tl;dr
works:
{{ post.choices | json }}
does not work:
<li v-for="(value,key) in post.choices | json">
Any work around? I can't use computed properties because this is a "sub-object" of an array and computed properties don't work that way, unless I am mistaken?

You cannot add a filter to a v-for directive.
Just parse the data before setting the posts data property:
HTTP.get('questions', { headers: { 'Api-Key': this.apiKey} })
.then(response => {
let posts = response.data;
post.forEach(p => p.choices = JSON.parse(p.choices));
this.posts = posts;
})

I don't see why you need to use JSON.parse, since the choices key refers to a valid JS object. Unless you have made a mistake, and that the choices key refers to a string.
Anyway, if you want to parse post.choice into a valid JS object, you can simply pass it into a custom function which returns the parsed JSON to v-for, for example:
<li v-for="(value,key) in postChoicesJson(post.choice)">
{{value}} {{key}}
</li>
And then declare a method for that:
postChoicesJson: function(obj) {
return JSON.parse(obj);
}
Note: Your DOM is invalid, because <li> elements must be a direct child of a <ul> or an <ol> element.

Related

AlpineJS async call to render array of objects

Having the following snippet trying to fill an array of object with async alpine call but I can not get any result. Hier is what I try.
HTML:
<div x-init="experts.retrieveList" x-data="experts.list">
<ul>
<template x-for="item in experts.list" :key="item">
<li>
<div x-text="await item.address" ></div>
</li>
</template>
</ul>
</div>
external JS file
window.experts = {
apiUrl: 'http://bdb.local:8991/api/',
data: [],
list: [],
expertsForm: null,
retrieveList: () => {
const membersUrl = `${experts.apiUrl}members?include=user,association,affiliate`;
experts.apiCalls(membersUrl)
},
filterByParams: () => {
},
apiCalls: async (url) => {
let response = await fetch(url);
experts.list = await response.json()
return experts.list;
},
}
What is wrong in this case?
There are a few errors in this code:
You cannot use just a part of an Alpine.js component in the x-data="experts.list". If you don't define data variables directly in x-data, then it must be a real component that returns data and methods.
You cannot use an object as the key. It must be a string or number, like item.id or something like this.
The x-text="await item.address" seems incorrect. item.address should be a string, that has been already downloaded from the API.
In the component you need to use the this. prefix to access properties and methods.
Assuming your API returns the correct data format, something like this should work:
<div x-data="experts">
<ul>
<template x-for="item in list" :key="item.id">
<li>
<div x-text="item.address"></div>
</li>
</template>
</ul>
</div>
And the component in an external file:
const experts = {
apiUrl: 'http://bdb.local:8991/api/',
data: [],
list: [],
expertsForm: null,
init() {
const membersUrl = `${experts.apiUrl}members?include=user,association,affiliate`
this.apiCalls(membersUrl)
},
filterByParams() {
},
async apiCalls(url) {
let response = await fetch(url)
this.list = await response.json()
},
}
The init() method is executed automatically by Alpine.js.

VueJs/Nuxt Filter an array, error _vm.filtered.. is not a function

I am new to Vue/Nuxt and try to filter an array.
computed: mapState({
entries: state => state.archives.archives
}),
filteredArchive, function (objects, key) {
if (objects) {
return objects.filter(function(object) {
return object.tag === key
})
}
I want to get the result in a loop:
<li v-for="(entry, index) in (entries | filteredArchive('test'))">{{ entry.title }}</li>
This fails..
What is wrong in my approach..
Thanks for help.
I suggest creating a computed property for "filteredArchive" instead of a filter. In fact, I believe filters are going away in Vue 3. You can put it in a mixin if you need to share the logic across components.
ok. this is my solution for now:
<ul>
<li v-for="(entry, index) in filteredByTag(entries, 'test')">
<nuxt-link :to="'archive/' + entry.id">{{ entry.title }}</nuxt-link>
</li>
</ul>
computed: mapState({
entries: state => state.archives.archives,
}),
methods: {
filteredByTag(entries, tag){
return entries.filter((entry) => {
return entry.tag.match(tag)
})
}
},

How can i get properties from template

Can i get properties from template before send request to server about the data?
Here's my template:
<box-component inline-template>
<div>
<div class="loading" v-if="Loading"> Loading ... </div>
<div v-if="Result">
<ul>
<li> {{ Result.FirstProp }} </li>
<li> {{ Result.FourthProp }} </li>
</ul>
</div>
</div>
And this is my component:
let BoxComponent = {
data() {
return {
Result: null,
Loading: true
};
},
created: function () {
this.Update();
},
methods: {
Update: function () {
this.Loading = true;
axios.post("#SERVER_URL#")
.then(response => {
this.Loading = false;
this.Result = response.data;
});
}
}
}
Vue.component('box-component', BoxComponent);
It's works fine! The problem is the server response contain much more data. Server response:
{
"FirstProp": "first",
"SecondProp": "second"
...
"HundredthProp": "hudredth"
}
I can't modify the response (other project use this too). But if i am able to send property list from the template (which are in this case FirstProp and FourthProp) the server side give me the filtered response which contains only these properties becouse the optimal response look like this:
{
"FirstProp": "first",
"FourthProp": "fourth"
}
So, the question is: how can i do that?
Can i find it in the Vue object?
I can load template as a string variable, but i want to avoid regex hack.
Update:
In this case, BoxTemplate call server side without "filters"
This is the optimal case, when js get the variables, that the template use, and call with them the server side. In this case, in the response there are only that variables, what template really use
I don't know how is set up your back-end but you can have additional property within your data:
data() {
return {
listOfProperty: [
'FirstProp',
'FourthProp',
],
...
And use list of it to send data to server.

How to get id for axios request?

I have a getVueItems mehod that return products:
getVueItems: function(page){
axios.get('./api/home/product').then(response => {
this.items = response.data;
});
},
<div v-for="item in items" :key="item.id">
.
.
.
<a #click="AddCart()"></a>
</div>
as you see i have a another method that name is AddCart, this method is independent and must running a unique url to add the item to cart:
AddCart() {
axios.get( `./add-to-card/1`)
.then((response) => {
this.$store.dispatch('CartDetail')
});
},
in AddCart() axios url i need to get the id of product,how can i do this?
You have access to item in the scope of your v-for tag/block. Just do <a #click="AddCart(item.id)"></a>, and receive the argument with AddCart(id) {}

Why does axios delete not work as I want, how to fix it?

I took the data from https://jsonplaceholder.typicode.com/posts, I got it out normally, but I also want to create a button when I click on it, the post will be deleted. I wrote the code, but it does not work, can you tell me what is the error? When you click on the button in the console writes 'delete', but the post remains.
Screenshot of console
<template>
<div id="app">
<ul>
<li v-for="post of posts">
<p>{{ post.title }}</p>
<p>{{ post.body }}</p>
<button #click="deleteData(post._id)">Delete</button>
</li>
</ul>
</div>
</template>
<script>
import axios from 'axios';
export default {
name: 'app',
data () {
return{
posts: [],
}
},
created(){
axios.get('http://jsonplaceholder.typicode.com/posts').then(response => {
this.posts = response.data
})
},
methods: {
deleteData(_id) {
axios.delete('http://jsonplaceholder.typicode.com/posts/' + id)
.then(response => {
console.log('delete')
this.posts.splice(
this.posts.findIndex(e => e.id === id)
)
})
.catch(function(error) {
console.log(error)
})
},
}
}
</script>
Your approach is good, but you're using methods in wrong way.
There are two things you have to remember.
First, your post variable is an array.
API gives you json data, and what you have to do is to push that data into your array, instead of using = operand
Secondly, splice(index) just returns the same object.
Using splice(index, 1) instead.
It will delete 1 post from that index.