Dynamic collapse component not working in bootsrap-vue - vuejs2

I am trying to add a collapse component from bootstrap-vue into a table I am creating (Not bootstrap-vue table as find it easier to use plain table)
Can anyone tell me why this works as expected (But obviously opens every collapse component with the same ID)
<td>
<fa icon="bars" v-b-toggle.collapse2/>
</td>
</tr>
<td colspan="4">
<b-collapse id="collapse2" >
<b-card>
<p class="card-text">Collapse contents Here</p>
</b-card>
</b-collapse>
</td>
But this doesn't work, I know i have a unique id in case.id
<td>
<fa icon="bars" v-b-toggle="'collapse-' + case.id" />
</td>
</tr>
<td colspan="4">
<b-collapse id="'collapse-' + case.id" >
<b-card>
<p class="card-text">Collapse contents Here</p>
</b-card>
</b-collapse>
</td>
Many Thanks

You are not setting up a proper attribute binding in id="'collapse-' + case.id". It works in v-b-toggle="'collapse-' + case.id" case because as stated in the docs
Directive attribute values are expected to be binding expressions
In case of attributes you should use one of the following:
mustache
<div id="item-{{ id }}"></div>
v-bind
<div v-bind:id="'item-' + id"></div>
shorthand :
<div :id="'item-' + id"></div>

Related

How to ignore that clicking on a TD tag does not automatically redirect me to another view? Vue

I have a table where in each TR when I click it redirects me to another view with the detail, the idea is that I have never worked with Vue and I can't think how to disable the event when I click on the first TD tag of the table.
Before in Jquery I did it this way:
//Add onclick to TR
$('table tr').click(function(e) {
// Avoid redirecting if it's a delete button
if(!$(e.currentTarget).hasClass('delete')) {
// Not a button, redirect by taking the attribute from data-route
window.location.href = $(e.currentTarget).data('route');
}
});
But with Vue I don't know how to do it in the onclick event.
This is my table and the method
<table
id="accounts-table"
class="
table table-listbox
table-bordered_
table-responsive-md
table-striped_
text-center_
"
>
<thead>
</thead>
<tbody>
<tr v-if="tableData.length === 0">
<td colspan="4" class="text-center">
No hay oportunidades para mostrar.
</td>
</tr>
<template v-if="loading === true">
<tr colspan="9" class="text-center">
<b-spinner variant="primary" label="Spinning"></b-spinner>
<b-spinner variant="primary" type="grow" label="Spinning"></b-spinner>
</tr>
</template>
<template v-else>
<tr
v-for="(data, k) in tableData"
:key="k"
#click="viewOpportunity(k, data)"
>
<slot :row="data">
<td v-if="permissions.includes('view all opportunities')" width="10">
<div class="iq-email-sender-info">
<div class="iq-checkbox-mail">
<div class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" id="mailk">
<label class="custom-control-label" for="mailk"></label>
</div>
</div>
</div>
</td>
<td width="20">
<vue-avatar
:username="data.sales_man"
:title="data.sales_man"
:size="32"
v-if="data.sales_man != ''"
></vue-avatar>
</td>
<td width="5">
<template v-if="data.has_file != false">
<i
class="ri-attachment-line"
style="font-size: 20px; color: #007bff"
></i>
</template>
</td>
<td width="120" nowrap>
<template>
<p class="mb-0">{{ data.created_at }}</p>
<small class="text-info mt-5">
Fecha creación
</small>
</template>
</td>
</slot>
</tr>
</template>
</tbody>
</table>
viewOpportunity(data) {
window.location.href = "/opportunity/" + data.id;
},
For example in the first TD of the table I would like that it does not redirect me to the page but from the 2nd TD it redirects me.
On first td element use this even modifier - #click.stop
Prevent on click on parent when clicking button inside div
This kind of issue ever answered .. please have a look on this
If someone needs an href inside a table that has a that has a #click method in the row, but you wat to the link does not fire the click event in the row and also to prevent event propagation when there is no method to call, e.g. a simple <a href="mailto:someone#example.org"> link, try to use this trick with #click.self:
BEFORE
<table>
<tr>
<th>COLUMN A</th>
<th>COLUMN B</th>
</tr>
<tr #click="myMethodFromRow">
<td>Some Value</td>
<td>
<a target="_blank" :href="mailto:someone#example.org"> My link </a>
</td>
</tr>
</table>
AFTER
<table>
<tr>
<th>COLUMN A</th>
<th>COLUMN B</th>
</tr>
<tr>
<td #click="myMethodFromRow">Some Value</td>
<td #click.self="myMethodFromRow">
<a target="_blank" :href="mailto:someone#example.org"> My link </a>
</td>
</tr>
</table>
In this case I wanted to execute myMethodFromRow clicking inside each td, but not clicking the link. So I changed the myMethodFromRow method from the tr and put it into each td. But the most important trik was to use #click.self for the td with the href link.
.self only trigger handler if event.target is the element itself, i.e. not from a child element.
DOCUMENTATION: https://vuejs.org/guide/essentials/event-handling.html#event-modifiers

Add v-html attribute only if slot not given

Consider the following in a component snipet:
<tr v-for="row in rows" :key="row[rowKey]">
<td v-for="col in cols">
<slot v-if="col.bSlot" :name="col.bSlot" :row="row"></slot>
<template v-else v-html="formatField(row, col)"></template>
</td>
</tr>
Above I want to render a slot if given and if not to render a string returned by a formatting function unescaped. This did not work because I found out that v-html does not work with templates. The following works but you need extra unnecessary div tags that I don't want:
<td v-for="col in cols">
<slot v-if="col.bSlot" :name="col.bSlot" :row="row"></slot>
<div v-else v-html="formatField(row, col)"></div>
</td>
It would be nice if this still worked but has been deprecated:
<td v-for="col in cols">
<slot v-if="col.bSlot" :name="col.bSlot" :row="row"></slot>
<template v-else>
{{{formatField(row, col)}}}
</template>
</td>
The only other option I am left with is this but because there is no where to put the v-else I made the formatField() return nothing if a slot is given:
<td v-for="col in cols" v-html="formatField(row, col)">
<slot v-if="col.bSlot" :name="col.bSlot" :row="row"></slot>
</td>
formatField(row, col) {
if (col.bSlot) return; // also tried returning null, undefined, and ''
...
}
However now the slot doesn't render when one is provided. It seems like Vue ignores the slot if v-html is provided. So how do I not provide v-html when the col.bSlot is not provided?
You can place default value if slot is not given -
Fallback-Content
How about this:
1 <td v-for="col in cols">
2 <slot :name="col.bSlot" :row="row">
3 {{ formatField(row, col) }}
4 // <-- this is a comment
5 // <span v-html="formatField(row, col)"></span>
6 </slot>
7 </td>

Algolia & Vue InstantSearch: Customising widgets with slot

I am trying to modify the output of an InstantSearch widget for Vue.
In the documentation (https://www.algolia.com/doc/api-reference/widgets/hits/vue/#customize-the-ui) it says that using the scope-slot it will override the complete DOM output of the widget:
But it does not seem to be the case here. This is my code below using slot with a simple <tr> and <td> elements:
Instead of rendering a <tr> with <td> inside of it, I see here:
A div with a class of ais-Hits
A nested ol with a class of ais-Hits-list
A nested li with a class of ais-Hits-item
The output is this:
If I go to inspect element and I delete the elements I mentioned above (see how div, ol and li are deleted):
Then the result is correct:
Am I doing something wrong? Shouldn't slot override the DOM output and leave the rest to the developer to style?
Any help would be much appreciated!
You have to use the default slots rather than item. You'll have full control over the render.
<ais-hits>
<ol slot-scope="{ items }">
<li v-for="item in items" :key="item.objectID">
<ais-highlight :hit="item" attribute="name" />
<p>
<ais-highlight :hit="item" attribute="description" />
</p>
</li>
</ol>
</ais-hits>
Here is an example on CodeSandbox.
The ais-Hits will always wrap the default slot with a div (see GitHub for the explaination). The only alternative to avoid this issue is to create your own widget with the mixin createWidgetMixin:
<template>
<ol v-if="state">
<li v-for="item in state.hits" :key="item.objectID">
<ais-highlight :hit="item" attribute="name" />
<p>
<ais-highlight :hit="item" attribute="description" />
</p>
</li>
</ol>
</template>
<script>
import { createWidgetMixin } from "vue-instantsearch";
import { connectHits } from "instantsearch.js/es/connectors";
export default {
mixins: [createWidgetMixin({ connector: connectHits })]
};
</script>
Here is an example on CodeSandbox.
Although #Samuel Vaillant pointed me at the correct path, I believe the code below can help others with the same issue as me (populating HTML tables with Algolia results) without needing to create custom widgets:
<template>
<ais-hits>
<table slot-scope="{ items }">
<thead>
<tr>
<th>Name</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr v-for="item in items" :key="item.objectID">
<td>
<ais-highlight :hit="item" attribute="name" />
</td>
<td>
<ais-highlight :hit="item" attribute="description" />
</td>
</tr>
</tbody>
</table>
</ais-hits>
</template>

v-for render html table with multiple tr

I have a table where each "logical" row contains of two "markup rows".
<table class="table table-bordered">
<tbody>
<template v-for="(note, index) in notes">
<tr>
<td>
{{ moment(note.noteTime).format('YYYY-MM-DD HH:mm') }}
</td>
<td>
{{ note.locationName }}
</td>
</tr>
<tr>
<td colspan="2">
{{ note.noteText }}
</td>
</tr>
</template>
</tbody>
</table>
Is there a way generate this kind of table in vue without hurting html syntax (template element is not valid inside tbody)?
<template> does not generate a html element and thus will not interfere with html syntax.
There is actually a similar example inside the VueJS docs:
https://v2.vuejs.org/v2/guide/list.html#v-for-on-a-lt-template-gt
<ul>
<template v-for="item in items">
<li>{{ item.msg }}</li>
<li class="divider" role="presentation"></li>
</template>
</ul>
Here is a jsFiddle to see the example from the docs in action. You can inspect the HTML syntax:
https://jsfiddle.net/50wL7mdz/545901/

Vue v-if and remove span

Im using folowing code to render generic table:
<tr v-for="row in filteredData" >
<td v-for="column in visibleColumns">
<span v-if="column.type=='Date'" :class="column.cssClass">{{row[column.field] | formatDate}}</span>
<span v-else-if="column.type=='Bool'" :class="column.cssClass"><input #change="genericPost(column.url,row)" class="w3-check" type="checkbox" v-model="row[column.field]" ></span>
<span v-else-if="column.type=='Decimal'" :class="column.cssClass">{{row[column.field] | formatPrice}}</span>
<span v-else-if="column.type=='Button'" :class="column.cssClass"><a #click="clickItem(column.url,row)" class="w3-button w3-blue w3-padding-small">Edit</a></span>
<span v-else-if="column.type=='Link'" :class="column.cssClass"><router-link :to="{ path: row.route }">{{row[column.field]}}</router-link></span>
<span v-else :class="column.cssClass">{{row[column.field]}}</span>
</td>
</tr>
Is there any way to remove span element from conditional rendering ?
To have simple
<td class="xxx">value</td>
not <td><span class="xxx">value</span></td>
You can use v-for with child or sub <template> tags.
<tr v-for="row in filteredData" >
<td v-for="column in visibleColumns" :class="column.cssClass">
<template v-if="column.type=='Date'">{{row[column.field] | formatDate}}</template>
<template v-else-if="column.type=='Bool'" ><input #change="genericPost(column.url,row)" class="w3-check" type="checkbox" v-model="row[column.field]" ></template>
<template v-else-if="column.type=='Decimal'" {{row[column.field] | formatPrice}}</template>
<template v-else-if="column.type=='Button'" ><a #click="clickItem(column.url,row)" class="w3-button w3-blue w3-padding-small">Edit</a></template>
<template v-else-if="column.type=='Link'" ><router-link :to="{ path: row.route }">{{row[column.field]}}</router-link></template>
<template v-else >{{row[column.field]}}</template>
</td>
</tr>
In documentation: https://v2.vuejs.org/v2/guide/conditional.html#Conditional-Groups-with-v-if-on-lt-template-gt and https://v2.vuejs.org/v2/guide/list.html#v-for-on-a-lt-template-gt
FYI, it appears to be undocumented but it seems you can also use the <slot> tag in place of v-for with the <template> tags as well. Since I know there are no hidden "gotchas" with the template tag I just use it.