v-for with dynamic variable - vue.js

I am working on a Vue app with i18n and want to list items from a large JSON file in the selected languages.
Here is an example JSON file:
items: [
{
"name_en": "Name",
"name_de": "Name"
},
{
"year_en": "Year",
"year_de": "Jahr"
}
]
I would like to get the items with v-for like this:
<div v-for="(item, index) in data.items" :key="index">
<p {{item.name_[$i18n.locale]}} </p>
</div>
$i18n.local drops the current language code such as 'en' or 'de'. But what the correct syntax is to get the item.name_en or item.name_de?
Thank you for all your suggestion!

You're almost there: it's just the square brackets are misplaced + a bit of help with concatenating strings:
<p>{{ item['name_' + $i18n.locale] }}</p>
However, I am not the biggest fan of in-template string interpolation (personal preference). You can consider offloading/abstracting that logic into a computed property:
computed: {
i18nameKey() {
return `name_${this.$i18n.locale}`;
}
}
Then, you can use i18nameKey in your template as such:
<p>{{ item[i18nameKey] }}</p>

Related

How to search within nested objects

I have done my research trying to figure out how to achieve what I am describing below, however I had no luck.
In my Algolia index, some records have nested objects.
For example, title and subtitle attributes are of the following format:
title:
{
"en": "English title",
"gr": "Greek title"
}
I would like to execute queries only for a specific subset (in our example "en" or "gr") of these attributes, withoute "exposing" any facet in the UI — language selection would ideally be done “automatically” based on a variable (lang) passed to the Vue component with props. I am using Laravel Scout package with default Vue implementation, as described in documentation here.
My InstantSearch implementation is pretty simple, I am not defining anything specific regarding queries and searchable attributes, I am currently using all the default functionality of Algolia.
<template>
<ais-instant-search
:search-client="searchClient"
index-name="posts_index"
>
<div class="search-box">
<ais-search-box placeholder="Search posts..."></ais-search-box>
</div>
<ais-hits>
<template
slot="item"
slot-scope="{ item }"
>
<div class="list-image">
<img :src="'/images/' + item.image" />
</div>
<div class="list-text">
<h2">
{{ item.title }}
</h2>
<h3>
{{ item.subtitle }}
</h3>
</div>
</template>
</ais-hits>
</ais-instant-search>
</template>
<script>
import algoliasearch from 'algoliasearch/lite';
export default {
data() {
return {
searchClient: algoliasearch(
process.env.ALGOLIA_APP_ID,
process.env.ALGOLIA_SEARCH
),
route: route,
};
},
props: ['lang'],
computed: {
computedItem() {
// computed_item = this.item;
}
}
};
</script>
I would like to somehow pass an option to query “title.en” and “subtitle.en” when variable lang is set to “en”. All this, without the user having to select “title.en” or “subtitle.en” in the UI.
Update
Maybe computed properties is the path to go, however I cannot find how to reference search results/hits attributes (eg item.title) within computed property. It is the code I have commented out.
I think, you can use computed property. Just transform current item according to the current language variable.
new Vue({
template: "<div>{{ computedItem.title }}</div>",
data: {
langFromCookie: "en",
item: {
title: {
en: "Hello",
ru: "Привет"
}
}
},
computed: {
computedItem() {
const item = JSON.parse(JSON.stringify(this.item));
for (value in item) {
if (typeof item[value] === "object" && Object.keys(item[value]).includes(this.langFromCookie))
item[value] = item[value][this.langFromCookie];
}
return item;
}
}
}).$mount("#app")
<div id="app"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
If lang variable is available via props, you can check that inside list-text class and return {{title.en}} or {{title.gr}} accordingly by passing a dynamic lang value title[lang] like below
...
<div class="list-text">
<h2>
{{ item.title[lang] }}
</h2>
<h3>
{{ item.subtitle[lang] }}
</h3>
</div>
If you want to make a request according to lang prop when component mounts ,then you can make a request inside mounted() method then query like below
mounted() {
axios.get(`/getSomethingWithLang/:${this.item.title[this.lang]}`)
...
}

Using Slots or slot-scopes in v-for loops to access properties?

I'm having a difficult time understanding slots for some reason and why they should even be used. The only reason I can think of that would be nice for usuage is if we can reference specific properties within a v-for loop of an element and output different templates quicker perhaps...
So, am thinking, and possibly I could be wrong in thinking this, but if I have a variable like so:
<script>
const items: [
{
label: 'My Label',
url: '#',
headerTitle: 'My Header Title'
},
{
label: 'My Label 2',
url: 'https://www.myurl.com',
headerTitle: 'My Header Title 2'
},
{
label: 'My Label 3',
url: 'https://www.myurl3.com'
}
]
export default {
data () {
return {
items: items
}
}
}
</script>
And than in the template, possibly this:
<template>
<div v-for="(item, index) in items" :key="item.id">
<template slot-scope="headerTitle">
<h1>{{ item.headerTitle }}</h1>
</template>
<template slot-scope="label">
<div class="mylabel">
{{ item.label }}
</div>
</template>
<template slot-scope="url">
<a :href="item.url">{{ item.label }}</a>
</template>
</div>
</template>
I don't know if this makes sense or not, but basically using the property as a slot-scope and than for everytime that property is defined, it will output something. But this doesn't work properly. Is this not what slot-scopes are for within component v-for loops? Is this not how to use these properties of an array of objects?
This kinda makes sense to me. Anyways to do it like this perhaps?

Vue js how render curly brackets variables with in v-for loop

in my data object
items: [
{ name: "Breakfast", comp: "breakfastItems" },
{ name: "Lunch", comp: "lunchItems" },
{ name: "Dinner", comp: "dinnerItems" },
{ name: "Dessert", comp: "desertItems" }
]
where comp is a computed property.
in my component template I want to achive something like this using a for loop.
<span v-for="n in items">
{{n.comp}}
</span>
this doesn't work because I need to add {{}} when it's rendering. How do I do that?
To bind computed properties inside your template via dynamic interpolation, you can use the $root variable.
Assuming that the comp properties you've listed are collections underneath, the template might look like:
<span v-for="n in items">
<span v-for="m in $root[n.comp]">{{ m }}</span>
</span>
Here's a demonstration of the suggestion.

Vuejs v-for set unique data

I have a component that I can add when I click on the button.
<button type="submit" #click="components ++">add select box</button>
<div v-for="component in components">
<select class="selectpicker form-control" v-model="customized_exercise.name" value={{exercise.id}}>
<option v-for="exercise in exercises">{{ exercise.name }}</option>
</select>
</div>
In this template when I add a couple of them and select a value,all the other components (select box) update with the same value.How can I make them have a unique value?
Vue
import ExerciseSelectbox from './ExerciseSelectbox.vue'
export default {
methods: {
fetchexercises: function(){
this.$http.get('/api/exerciseinstructions').then(function (data) {
this.$set('exercises',data['data'])
})
},
},
comoponents: { ExerciseSelectbox },
data() {
return{
components: 1,
newCustomizedExercise : {
id:'',
name:'',
reps:'',
sets_duration:'',
weight:'',
},
numbers:[100]
}
},
ready() {
this.fetchexercises()
}
}
This is old, but I was also looking for this answer. I found that the Vue documentation mentions this:
Inside v-for blocks we have full access to parent scope properties.
v-for also supports an optional second argument for the index of the
current item.
<ul id="example-2">
<li v-for="(item, index) in items">
{{ parentMessage }} - {{ index }} - {{ item.message }}
</li>
</ul>
So you can use this index parameter to set the id of each element in your loop to keep each one separate.
Or you can set the set your model to a name in the array. Something like v-model="customized_exercise[index].name". But I haven't actually tried this option.
I found another SO answer about how to use v-bind if you want to string concatenate the index variable in a data attribute.
<a v-bind:href="'uniqueName' + index">12312</a>

VUEJS remove Element From Lists?

it is possible to remove specific element from lists. i tried this functions for remove element
pop() = remove last element
$remove(index) = not remove any element from lists
remove( index ) = undefined function
unshift( index ) = add new and empty element
splice( index ) = remove all element from index
please help me to remove specific element from lists.
below is my js code
var example2 = new Vue({
el: '#example-2',
data: {
items: [
{ message: 'Foo' },
{ message: 'Bar' },
{ message: 'Bar1' },
{ message: 'Bar2' },
{ message: 'Bar3' },
{ message: 'Bar4' }
]
},
methods : {
removeElement : function(index){
this.items.$remove(index);
}
}
})
below is my HTML code
<ul id="example-1">
<li v-for="(key, item) in items">
{{ item.message }}
<button v-on:click="removeElement(key)">remove</button>
</li>
</ul>
$remove is deprecated in Vue.js 2.0 and replaced by splice as stated in the docs. Make sure you add the 2nd parameter of splice for it to work.
Migration From Vue 1.x - 2.0
methods: {
removeElement: function (index) {
this.items.splice(index, 1);
}
}
You can use Vue.delete if your Vue version is 2.2.0+
Vue.delete(this.items, index);
The $.remove feature has been replaced with $.delete.
You can call it like so:
this.$delete(this.someItems, itemIndex).
It works on Object as well as Array. With objects, you need to use a keyed object. With arrays, you pass in the index of the item you want to delete.
Here is a fiddle example: https://jsfiddle.net/james2doyle/386w72nn/
EDIT
I added an example for an array as well
$delete can use inline in #click:
<ul id="example">
<li v-for="(item, key) in items">
{{ item.message }}
<button #click="$delete(items, key)">remove</button>
</li>
</ul>
https://v2.vuejs.org/v2/api/#vm-delete
Firstly, you should fix the methods key.
Then, you should pass the item to the $remove method, not the index. [ref]
https://jsfiddle.net/790og9w6/
If anyone wonders: $delete has been removed in VueJS 3 ( https://v3-migration.vuejs.org/breaking-changes/introduction.html#removed-apis ). Now it is possible to use javascripts native splice(index, count of objects to be deleted).