v-for look using curly brackets - vue.js

I ran into a situation that I need to loop true items without producing any HTML. I expect the code to look something like this.
<table id="detailTable">
<tr>
<th class='editRow'></th>
<th class='editRow'></th>
<!-- <th class='editRow'></th> -->
<th v-for='(columns, index) in $parent.columns' :key='index'>{{ firstLetterCaps(columns) }}</th>
</tr>
{{ for (row, index) in $parent.results }}
<resultsRows v-for='(row, index) in $parent.results' :key='index' :row='row' :index='index' :deleteQueryObjects='$parent.deleteQueryObjects'></resultsRows>
<resultsCommentRow v-for='(row, index) in $parent.results' :index='index'></resultsCommentRow>
{{ end-for}}
</table>
To make it clear I expect instead of using <div v-for=item in items></div> to this { for item in items } { end for } does this exist for vue ?

There is no such a syntax in vuejs, if you want to loop on something without touching it you can use <template> like :
<template v-for="element in elements" >
...
</template>
template tag description from : https://www.w3schools.com/TagS/tag_template.asp
you can go deeper : https://v2.vuejs.org/v2/guide/syntax.html
In your case :
<template v-for='(row, index) in $parent.results' >
<resultsRows :row='row' :index='index' :deleteQueryObjects = '$parent.deleteQueryObjects' />
<resultsCommentRow :index='index' />
</template>

Related

How to emit function with id from child component to parent in vue.js

I have a card component and a house component . I want to emit function along with id from child to parent component.I want to call edit function from card component which is in house component.
card.vue
<tr class="registrationtable" v-for="valueList in values" v-bind:key="valueList">
<td class="registrationtable" v-for="head in columnHeader" v-bind:key="head">
<span v-if="href.includes(head)">
{{ head }}
<a
v-for="action in actionList"
v-bind:key="action"
v-on:click="methodToCall(valueList[head])"
>{{ action }}</a
></span
>
<span v-else></span>
</td>
</tr>
created() {
this.methodToCall(id);
},
house.vue
<card :method-to-call="editFunction" />
You can use this.$emit()
card.vue template
<tr class="registrationtable" v-for="valueList in values" v-bind:key="valueList">
<td class="registrationtable" v-for="head in columnHeader" v-bind:key="head">
<span v-if="href.includes(head)">
{{ head }}
<a
v-for="action in actionList"
v-bind:key="action"
v-on:click="methodToCall(valueList[head])"
>{{ action }}</a
></span
>
<span v-else></span>
</td>
</tr>
card.vue
methods:{
methodToCall:function(id){
this.$emit('methodToCall', id)
}
}
house.vue
<card :method-to-call="editFunction" />

Vue - change/set a variable value in template

Working in Vue, I am trying to set a variable based on another variable within the template. This is within a loop, and I need to set a value that can be used in 'next' iteration of the loop (to change the way a table is rendered, based on the variable).
I have the following:
<template>
...
<tbody v-for="(lo,index) in learn" :key="lo.id">
<tr>
<td colspan="2">LO{{index+1}} {{lo.attributes.field_lo}}</td>
<td v-if="!nextDist"> </td>
</tr>
<tr>
<td>
<div
v-for="(pass,index) in lo.attributes.field_pass"
:key="index"
>P{{pStep()}} {{pass}}</div>
</td>
<td>
<div
v-for="(merit,index) in lo.attributes.field_merit"
:key="index"
>M{{mStep()}} {{merit}}</div>
</td>
<td v-if="lo.attributes.field_dshared && next" ***SET VALUE OF this.next*** rowspan="3">
<span class="has-text-weight-bold">D{{dStep()}} </span>{{lo.attributes.field_dist}}
</td>
<td v-else-if="!lo.attributes.field_dshared" ***SET VALUE of this.next*** ><span class="has-text-weight-bold">D{{dStep()}} </span>{{lo.attributes.field_dist}}
</td>
***else render nothing***
</tr>
</tbody>
</template>
export default {
name: "SpecUnit",
components: {
EssentialContent
},
data() {
return {
unit: "",
learn: "",
nextDist: "",
next: ""
};
},
...
}
What I'd like to be able to do is set the value of 'next' (this.next) so that when the loop iterates, I can check to see if I should which of the I should render or render nothing (because we are 'rowspanning').
I've tried computed and methods, but can't seem to get this working. I've looked to use Vue.set, but I'm struggling with that.
I'm still new to Vue, so any help would be greatly appreciated.
Thanks
It looks like Florian Reuschel had a similar problem and already solved it (although with some caveats)
Let's say we have something like that:
<!-- List.vue -->
<ul>
<li v-for="id in users" :key="id">
<img :src="getUserData(id).avatar"><br>
🏷️ {{ getUserData(id).name }}<br>
🔗 {{ getUserData(id).homepage }}
</li>
</ul>
His approach is to use a helper renderless component with a scoped slot
const Pass = {
render() {
return this.$scopedSlots.default(this.$attrs)
}
}
and then
<!-- List.vue -->
<ul>
<Pass v-for="id in users" :key="id" :metadata="getUserData(id)">
<li slot-scope="{ metadata }">
<img :src="metadata.avatar"><br>
🏷️ {{ metadata.name }}<br>
🔗 {{ metadata.homepage }}
</li>
</Pass>
</ul>
If you take a look at the comments section on his blog article, you will see other approaches, too. For example, you can use an expression inside v-bind
<li v-for="id in users" :key="id" :demo="item = getUserData(id)">
<img :src="item.avatar" /><br />
🏷️ {{ item.name }}<br />
🔗 {{ item.homepage }}
</li>

Does vue.js have something like AngularJS' `ng-repeat-start`

Parsing vue.js' docs I can't see any hint that vue.js has something like a ng-repeat-start / ng-repeat-end
Is there a way to archive something like
<table>
<tr class="weather_warning top" ng-repeat-start="warning in dwd_warnings">
<td {{warning.valid_from}} → {{warning.valid_to}}</td>
<td><div style='background:{{warning.color}};'>{{warning.message}}</div></td>
</tr>
<tr class="weather_warning bottom" ng-repeat-end>
<td colspan="2">
{{warning.description}}<br/>
<span>Issued on the {{warning.effective}} ({{warning.headline}}). {{warning.ascertain}}</span>
</td>
</tr>
</table>
Each warning consists of 2 TRs which go together. One is the top half of the message in the iteration, the other one the lower half. It must be this way, because of some very weird formatting stuff, so there's no way around using 2 TRs for one item. Maybe some tags are off in this example, this is because I had to delete a ton of stuff in order to post it here. Note the colspan="2" in the TD of the second TR, which might give a bit of a hint of the problem which is tackled here.
Closest thing I can think of is to use a <template>, eg
<template v-for="item in items">
<header>
Header {{ item }}
</header>
<div class="body">
Body {{ item }}
</div>
<footer>
Footer {{ item }}
</footer>
</template
See https://v2.vuejs.org/v2/guide/list.html#v-for-on-a-lt-template-gt
... you can also use a <template> tag with v-for to render a block of multiple elements
Nothing changes with your edit, you just wrap your two <tr> elements in
<template v-for="warning in dwd_warnings">
<tr class="weather_warning top">
<!-- etc -->
</tr>
<tr class="weather_warning bottom">
<!-- etc -->
</tr>
</template>

Vue: How to conditionally render tr in tbody

I have a table body with multiple rows, such as this:
<table>
<tbody>
<tr>...</tr>
<tr>...</tr>
</tbody>
</table>
I want to conditionally combine v-if an v-for, to conditionally render one or more additional rows. The Vue manual says to wrap the v-for in a v-if, such as follows:
<div v-if="team.positions != null">
<my-row v-for="position in team.positions"
:position="position"
:key="position.id">
</my-row>
</div>
The problem is that I can't put a div in a tbody, or any other element for that matter. What's the solution?
In those situations where no element would fit, you can use <template>, like:
<template v-if="team.positions != null">
<my-row v-for="position in team.positions"
:position="position"
:key="position.id">
</my-row>
</template>
Demo:
new Vue({
el: '#app',
data: {
showTwoRows: true
}
})
<script src="https://unpkg.com/vue"></script>
<div id="app">
<table>
<tr>
<td>A</td><td>B</td>
</tr>
<template v-if="showTwoRows">
<tr>
<td>1</td><td>2</td>
</tr>
<tr>
<td>3</td><td>4</td>
</tr>
</template>
<tr>
<td>C</td><td>D</td>
</tr>
</table>
<button #click="showTwoRows = !showTwoRows">Toggle two middle rows</button>
</div>
Though in that specific example of yours, it doesn't seem needed. Have you tried simply not using the v-if:
<my-row v-for="position in team.positions"
:position="position"
:key="position.id">
</my-row>
Because the v-for just won't iterate (without throwing errors) if its value is undefined/null/0/[]/'':
new Vue({
el: '#app',
data: {
message: "If I'm being displayed, Vue works!",
team: {
positionsU: undefined,
positionsN: null,
positionsZ: 0,
positionsE: [],
positionsS: ''
}
}
})
<script src="https://unpkg.com/vue"></script>
<div id="app">
<p>{{ message }}</p>
<table>
<tr v-for="position in team.positionsU"><td>u: {{ position }}</td></tr>
<tr v-for="position in team.positionsN"><td>n: {{ position }}</td></tr>
<tr v-for="position in team.positionsZ"><td>z: {{ position }}</td></tr>
<tr v-for="position in team.positionsE"><td>e: {{ position }}</td></tr>
<tr v-for="position in team.positionsS"><td>s: {{ position }}</td></tr>
<tr v-for="position in team.positionsF"><td>f: {{ position }}</td></tr>
</table>
</div>
You can use v-for and v-if on the same tag, however, it works differently to how you'd expect it to.
within the v-if you can reference the iterated item since v-for is performed before v-if
<div v-if="team.positions != null">
<my-row v-for="position in team.positions" v-if="position"
:position="position"
:key="position.id">
</my-row>
</div>
this would still iterate through all positions in team.positions, and not halt the for loop if the condition in the v-if was not met, but rather skip it.
think of it like this:
for (var i = 0; i < array.length-1; i++) {
if (array[i]) {
doTheThing();
}
}
I am not sure if this is exactly what the original question is looking for, but I just had a similar issue where I wanted to ignore rendering rows where the price of a item was 0.
I ran into the problem using v-if in the <tr> containing the v-for. I solved it by simply using a v-show instead.
So this worked perfectly in my case.
<tr v-show="item.price !== 0" :key="item._id" v-for="item in items"> ... </tr>

Child component not updating when data chaining - why does :key need to be the value that changes?

I had a table row that was like this:
<tr v-for="(pricing, idx) in pricings"">
<td>{{pricing.description}}</td>
<td>$ {{pricing.unconfirmed_price_formatted}}
</tr>
I wanted to migrate this table row to a component. However, when I change the underlying data (this.pricings), the child component doesn't update. I am calling it like this:
<pricing-row v-for="(pricing, idx) in pricings" :key="pricing.id + pricing.description" :pricing=pricing v-on:updateAfterSave="savedData" v-on:showModal="showAddEditModal"></pricing-row>
The strange thing is that the underlying array is changing - just this component is not properly updating.
It's also clear that if we use as a key the value that changes (unconfirmed_price_formatted in this case), it does update.
I'm a bit baffled by this behavior. Any ideas on what is wrong?
edit 1
here is the component:
<template>
<tr>
<td>{{localPricing.description}}</td>
<td v-if="localPricing.price">$ {{localPricing.price_formatted}} {{localPricing.units_display}}</td>
<td v-else> </td>
<td v-if="localPricing.unconfirmed_price">$ {{localPricing.unconfirmed_price_formatted}} {{localPricing.unconfirmed_units_display}}</td>
<td v-else> </td>
<td v-if="localPricing.state != 'deleted'">
<!-- button class="btn btn-outline-secondary" #click="willShowModalWithBattery(pricing)">edit</button -->
<button class="btn btn-outline-secondary" #click="showModal(pricing)">edit</button>
</td>
<td v-else> </td>
</tr>
</template>
<script>
export default {
props: ['pricing'],
data: function(){
return {
localPricing: this.pricing
}
},
methods:{
showModal: function(pricing){
this.$emit('showModal', pricing);
}
}
}
</script>