Conditional #click for <th> in VueJS - vue.js

How can I conditionally enable/disable #click with v-if/v-else while iterating through with v-for on a <th>?
I have the following code:
<template>
<div id="TableHeaderDiv" class="TableHeaderClass">
<table cellpadding="0" cellspacing="0" border="0">
<thead>
<tr>
<th
v-for="column in columns"
v-bind:key="column.DataField"
#click="sortBy(column.DataField)"
:class="{ active: sortKey == column.DataField }"
:style="{ width: column.Width }"
>
{{ column.DisplayText }}
<div v-if="!column.isControl">
<span
class="arrow"
:class="sortOrders[column.DataField] > 0 ? 'asc' : 'dsc'"
></span>
</div>
</th>
</tr>
</thead>
</table>
</div>
</template>
I would like to write a condition so that v-if="!column.isControl" ... add the #click to the <th>.
My initial approach was to do something like this:
<template>
<div id="TableHeaderDiv" class="TableHeaderClass">
<table cellpadding="0" cellspacing="0" border="0">
<thead>
<tr>
<div v-for="column in columns" v-bind:key="column">
<th
v-if="!column.isControl"
#click="sortBy(column.DataField)"
:class="{ active: sortKey == column.DataField }"
:style="{ width: column.Width }"
>
{{ column.DisplayText }}
<div v-if="!column.isControl">
<span
class="arrow"
:class="sortOrders[column.DataField] > 0 ? 'asc' : 'dsc'"
></span>
</div>
</th>
<th v-else :style="{ width: column.Width }">
{{ column.DisplayText }}
</th>
</div>
</tr>
</thead>
</table>
</div>
</template>
But this just created a handful of <div> elements which is not what I intend to do.
Perhaps I am missing a fundamental concept with conditional rendering. Any help is much appreciated!

If you are using Vue.js 2.6 or higher you can pass null to the #click directive, and thus click listener will not be bound.
More details can be found in this RFC. Conveniently, if the argument
value is null, the binding/listener will be removed.
So your code could look something like this:
v-on="{ [!colum.isControl ? 'click' : null]: sortBy(column.DataField) }"

Related

Showing table heading once in VueJS component

I have a search built in VueJS and the results of the search are displayed in a separate component. I want to display the data in a table with the table headings. The issue I'm having is that the table heading appears before each row. How do I get it to appear once? Im sure this is easy but I cant think how to do this. Im fairly new to Vue, so any help appreciated.
<table class="table table-hover" id="simpleTable">
<thead>
<tr>
<th
v-on:click="sortTable(title)"
:key="title"
scope="col"
class="col-md-3"
>
Course
</th>
<div
class="arcoursedata"
v-if="title == sorttitle"
v-bind:class="ascending ? 'arcoursedata_up' : 'arcoursedata_down'"
></div>
<th scope="col">Award</th>
<th scope="col">Level</th>
</tr>
</thead>
<tbody>
<tr>
<td class="col-md-3">
{{ title }}
</td>
<td>
{{ award }}
</td>
<td class="col-md-3">
{{ level }}
</td>
<td class="col-md-3">
<a
:href="result.displayUrl"
class="btn-sm btn-primary"
role="button"
>
<span class="sr-only">Read more about {{ title }}</span> Read
more</a
>
</td>
</tr>
</tbody>
</table>
You need to separate row from tbody to component. And do v-for only on this component and not on the entire table.
It will be look like:
<table class="table table-hover" id="simpleTable">
<thead>
<tr>
<th
v-on:click="sortTable(title)"
:key="title"
scope="col"
class="col-md-3"
>
Course
</th>
<div
class="arcoursedata"
v-if="title == sorttitle"
v-bind:class="ascending ? 'arcoursedata_up' : 'arcoursedata_down'"
></div>
<th scope="col">Award</th>
<th scope="col">Level</th>
</tr>
</thead>
<tbody>
<rowComponent v-for="rowData in tableData" :row="rowData"></rowComponent>
</tbody>
</table>
And RowComponent will look like:
<td class="col-md-3">
{{ row.title }}
</td>
<td>
{{ row.award }}
</td>
<td class="col-md-3">
{{ row.level }}
</td>
<td class="col-md-3">
<a :href="row.displayUrl"
class="btn-sm btn-primary"
role="button">
<span class="sr-only">Read more about {{ row.title }}</span> Read
more</a>
</td>
<script>
export default {
name: "rowComponent",
props: ['row']
}
</script>

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

How to iterate through an array in a Vue component?

I am trying to iterate through the array props_nfl_days which is equal to ["Sunday", "Thursday Night", "Monday Night"] so it appears as a header for each group of NFL scores by day of the week. The component looks like:
Updated Code:
const nfl = {
nflComponent: Vue.component("tab-nfl", {
props: [
"props_league_data_nfl",
"props_nfl_days",
"props_league_standings"
],
template: `
<div class="vue-root-element">
<div class="container nfl-scores">
<div v-for="(dayDataArray, index) in props_league_data_nfl">
<p> {{ index }} </p>
<h2> {{ props_nfl_days[index] }} </h2>
<div class="row">
<div class="col-xs-12 col-md-4 col-lg-3" v-for="arrayItem in dayDataArray">
<table class="table table-striped table-sm">
<thead>
<th scope="col" class="box-score-status is-completed" v-if="arrayItem.isCompleted">Final</th>
</thead>
<tbody>
<tr>
<td class="box-score-team"> {{ arrayItem.game.awayTeam.Abbreviation }} </td>
<td class="box-score-inning" v-for="quarter_score in arrayItem.quarterSummary.quarter">
{{quarter_score.awayScore }}</span>
<td class="box-score-final" v-bind:class="{ won: arrayItem.awayScore > arrayItem.homeScore }">{{ arrayItem.awayScore }}
</td>
</tr>
<tr>
<td class="box-score-team"> {{ arrayItem.game.homeTeam.Abbreviation }} </td>
<td class="box-score-inning" v-for="quarter_score in arrayItem.quarterSummary.quarter">
{{quarter_score.homeScore }}
</td>
<td class="box-score-final" v-bind:class="{ won: arrayItem.homeScore > arrayItem.awayScore }">{{ arrayItem.homeScore
}}
</td>
</tr>
<tr><td class="box-score-team w-150">Location: {{ arrayItem.game.location }} </td></tr>
</tbody>
</table>
</div> <!-- End v-for dayDataArray -->
</div> <!-- End row -->
</div> <!-- End v-for props_league_data_nfl -->
</div> <!-- End container -->
All I get is index goes to 0 and thats it. props_league_data_nfl is an objectwith three properties. Here is a snapshot of the output of Vue panel :
What I want is the correct nfl_days array element in h2 header for each props_league_data_nfl group. See picture:
I would like these Sunday headers to be Thurday Night and Monday Night respectively. Any guidance on how to achieve this much appreciated.
Computed properties should not have side effects. Computed properties are cached based on their dependencies; since increment doesn't see any changes in its dependencies, it does not re-compute its value. So increment it will run once and always return the same value: 0.
v-for takes additional parameters for the key (in the case of iterating over objet properties) and the current index, so you can remove the increment computed property and re-write your template like so:
<div v-for="(dayDataArray, key, index) in props_league_data_nfl">
**<h2> {{ props_nfl_days[index] }} </h2>**

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/

Style slot in vue

Have a Table call in Index.vue:
<utilization-table :title="table.title" :columns="table.columns" :rows="table.rows">
<template scope="data>
<td class="th_normal">{{data.row.name}}</td>
</template>
</utilization-table>
The Table Template is UtilizationTable.vue:
<template>
<div>
<h3 v-html="title"></h3>
<table class="table table-striped table-hover">
<thead>
<tr>
<th v-if="selectable" class="table-view-pf-select" aria-label="Select all rows">
<label>
<span class="sr-only">Select all rows</span>
<input type="checkbox" #change="changeSelectAll">
</label>
</th>
<th v-for="column in columns">{{column}}</th>
</tr>
</thead>
<tbody>
<utilization-table-row ref="row" v-for="(row, i) in rows" :key="i">
<slot :row="row"></slot>
</utilization-table-row>
</tbody>
</table>
</div>
</template>
With a Row as UtilizationTableRow.vue:
<template>
<tr role="row">
<slot></slot>
</tr>
</template>
It'd like to style the first "td" in the table to 130px. Since it is a slot, I'm not sure how to do this.
Normally, if my table is
<table>
<tr>
<td>
</td>
</tr>
</table>
I could just do:
<style> table tr td:first-child, table tr td:first-child {
width: 130px !important;
}
</style>
However, that isn't taking hold.