Vue.js / Algolia - Dynamically pass field names in template - vue.js

I am trying to dynamically show search results from Algolia, based on an array of names.
Input.vue:
<search-results title="Books" :fields="['booking_reference','shipment_reference']"></search-results>
Results.vue:
<template slot-scope="{ result }">
<h1 v-for="field in fields">{{ result.field }}</h1>
</template>
However, above code does not return anything in my template. It's just blank.
But my fields array does indeed have values:
And I can see the results from Algolia as well:
But it does not show the results.
If I edit the code and hardcode the field name I want to show, like this:
<template slot-scope="{ result }">
{{ result.booking_reference }}
</template>
I can see the result just fine in my template.
What am I doing wrong?
Update:
How can I do this with a multidimensional array?
My array:
fields:Array[2]
0:Object
maintitle:"booking_reference"
1:Object
subtitle:"shipment_reference"
I need to be able to access it like:
result.maintitle.field

try
<h1 v-for="field in fields">{{ result[field] }}</h1>
instead of
<h1 v-for="field in fields">{{ result.field }}</h1>

Related

Allow duplicated tags in bootstrap vue

I am in trouble with using duplicated tags in bootstrap Vue.
I am using the form tags in bootstrap Vue.
I cannot add the same value inside existing values.
['apple', 'orange', 'banana']
For example, I cannot add a new value of "apple" if the value of "apple" is already in the value array. This is because the form tag checks duplicated values and blocks to add them to the value array.
How can I add the same value to this array?
Here is the code I'm using:
<template>
<div>
<b-form-tags v-model="value" no-outer-focus class="mb-2">
<template v-slot="{ tags, inputAttrs, inputHandlers, tagVariant, addTag, removeTag }">
<b-input-group class="mb-2">
<b-form-input
v-bind="inputAttrs"
v-on="inputHandlers"
placeholder="New tag - Press enter to add"
class="form-control"
></b-form-input>
<b-input-group-append>
<b-button #click="addTag()" variant="primary">Add</b-button>
</b-input-group-append>
</b-input-group>
<div class="d-inline-block" style="font-size: 1.5rem;">
<b-form-tag
v-for="tag in tags"
#remove="removeTag(tag)"
:key="tag"
:title="tag"
:variant="tagVariant"
class="mr-1"
>{{ tag }}</b-form-tag>
</div>
</template>
</b-form-tags>
</div>
</template>
<script>
export default {
data() {
return {
value: ['apple', 'orange', 'banana']
}
}
}
</script>
This is because you are looping over a string array and using array elements as key. Please read this:
To give Vue a hint so that it can track each node’s identity, and thus reuse and reorder existing elements, you need to provide a unique key attribute for each item
Notice "you need to provide a unique key attribute for each item", but you are doing opposite and using array element as key, to handle this case you can always use the index from the loop, because index is unique for each element so:
<b-form-tag
v-for="(tag, index) in tags"
#remove="removeTag(tag)"
:key="index"
:title="tag"
:variant="tagVariant"
class="mr-1"
>{{ tag }}</b-form-tag>
</div>
Now this will not give you duplicate items error :)

Using generic value in v-slot:head

I try to create a generic Bootstrap Vue Table Component. For the field label in HTML format, I try to do something such:
<template v-for="field in fields" v-slot:head(field_name)>
<span v-translate render-html="true" v-html="field.label"></span>
</template>
Here, my problem is, when I put v-slot:head(field_name), the field_name is not generic and it is accepting as a string. So when I write field.key to v-slot:head, I want it to check the value of field.key but not field.key as the value itself.
<template v-for="field in fields" v-slot:head(field.key)>
<span v-translate render-html="true" v-html="field.label"></span>
</template>
Is there any way to handle this?
The syntax for dynamic slots is:
<template v-slot:[dynamicSlotName]>
// ...
</template>
Applied to your case:
<template v-for="field in fields" v-slot:[`head(${field.key})`]>
<span v-translate render-html="true" v-html="field.label"></span>
</template>

Vuetify data table - manually display expandable rows

I have a bunch of dynamic columns in a v-data table which I need to loop through and interrogate in order to display the correct info. It looks a bit like this (taken from the answer here: Vuetify format cell based on item type)
<v-data-table :item="items" ... >
<template v-for="header in headers" v-slot:[`item.${header.value}`]="{ item } ">
<template v-if="header.type === 'foo'">
<span style="color: red;">{{ item[header.value] }}</span>
</template>
<template v-else-if="header.value === 'data-table-expand'">
???
</template>
<template v-else>
{{ item[header.value] }}
</template>
</template>
</v-data-table>
Since I need the v-if statement, all other types default to the v-else. However, the v-else is not suitable for when a type is an expandable row. It will display a blank value for that column. So I created a v-else-if template to be able to capture the expandable row column and correctly render it to the screen.
The problem is that I don't know what to put in the template to indicate it's a column with expandable rows (https://vuetifyjs.com/en/components/data-tables/#expandable-rows). In other words it does not render the carat icon that shrinks/expands the subtable, nor does it render the clickable actions. How would I modify the v-else-if template to correctly render its contents?
I came up with a workaround using computed properties.
Instead of using
v-for="header in headers"
I changed it to a computed headers which is filtered.
<template v-for="header in headersIWant" v-slot:[`item.${header.value}`]="{ item } ">
<span style="color: red;">{{ item[header.value] }}</span>
</template>
...
computed: {
headersIWant() {
return this.headers.filter(x => x.type === 'foo');
}
}

Not binding the IF to any element in Vue

This works:
<span v-if="name">
Hi there, {{ name }}
</span>
... but it forces me to use span for the whole text, I just want it on the name variable. In handlebars for example I could do:
{{#if name}}
Hi there, <span>{{ name }}</span>
{{/if}}
You can use a template for that.
we can use v-if on a <template> element, which serves as an invisible
wrapper. The final rendered result will not include the <template>
element.
For example:
<template v-if="name">
Hi there, <span>{{ name }}</span>
</template>

VueJs v-for how to check list undefined before continue

Refer to the template below, how to add condition to make sure the menu is not undefined inside the v-for attribute?
I've tried v-for="menu?item in menu.items:[]" and v-for="item in menu?.items" but neither works.
<div v-for="item in menu.items">{{item.text}}</div>
Put the div with the v-for directive within a <template> that checks for menu via v-if:
<template v-if="menu">
<div v-for="item in menu.items">{{ item.text }}</div>
</template>
This way, the div inside the template won't be rendered if menu does not exist.
If you really want the check within the v-for statement, like you are attempting, it would look like this:
<div v-for="item in (menu ? menu.items : [])">{{ item.text }}</div>