V-for with template - vue.js

I am using vue-bootstrap. I try to create dynamic a grid component that gets headers and datas. Since we will not know how many column passed to the component, we should check every item that are passed.
<template>
<b-table striped hover :items="items"></b-table>
<div v-for="title in items">
<template slot="title.key" slot-scope="data">
<input v-if="title.isActive" type="text" v-model="data.value">
<textarea v-else type="text" v-model="data.value"></textarea>
</template>
</div>
</b-table>
</template>
<script>
const items =[
{'label': 'Description', 'key': 'description'},
{'label': 'Name', 'key': 'name', 'isActive': true},
]
So, if isActive is true, then this template should be textarea(Column should be changed with textarea instead of input) However it is not working and no columns changed neither inputbox nor textarea and stay default template
Could you please help on these question.
Thank you

I think you should separate title in v-for and slot-scope variable to avoid confusing:
<template v-for="title in items" :key="title.key">
<template :slot="title.key" slot-scope="item">
<input v-if="item.isActive" type="text" v-model="item.value">
<textarea v-else type="text" v-model="item.value"></textarea>
</template>
</template>

Since, the isActive property is present inside the object in items array, you need to access it as a property of an iteratee.
So the code becomes:
<div v-for="title in items">
<template slot="{{title.key}}" slot-scope="{title}">
<input v-if="title.isActive" type="text" v-model="title.value">
<textarea v-else type="text" v-model="title.value"></textarea>
</template>
</div>

Thanks to ittus's answer,after minor modification following way is worked.
<template v-for="column in columns" :slot="column.key" slot-scope="item">
<input v-if="item.isSelect" type="text" v-model="item.value">
<textarea v-else type="text" v-model="item.value"></textarea>
</template>
Thank you all.

You can check out this example for dynamic columns.
new Vue({
el: "#app",
data: {
fields: [{
key: "id",
label: "Id",
colType: "span"
}, {
key: "name",
label: "Name",
colType: "button"
}, {
key: "uhh",
label: "uhh..",
colType: "idk"
}],
items: [{
id: 0,
name: "Test 0"
}, {
id: 1,
name: "Test 1"
}, {
id: 2,
name: "Test 2"
}]
}
});
<div id="app">
<b-table :items="items" :fields="fields">
<template v-for="(field, index) in fields" :slot="field.key" slot-scope="data">
<div v-if="field.colType === 'button'">
<h5>{{data.item.name}}</h5>
<b-button>Am Button</b-button>
</div>
<div v-else-if="field.colType === 'span'">
<h5>{{data.item.name}}</h5>
<span>Am Span</span>
</div>
<div v-else>
<h5>{{data.item.name}}</h5>
Am Confused
</div>
</template>
</b-table>
</div>

I didn't get these answers to work. Which actually helped me, was this:
https://forum.vuejs.org/t/how-to-pass-cell-templates-to-a-component-with-b-table/106283
and this fiddle:
https://jsfiddle.net/skirtle/6of9dnpz/
toCellName (slot) {
return `cell(${slot})`
}
The key is that the toCellName returns the correct slot name.

You have to use below snippet.
<input v-if="title.isActive" type="text" v-model="title.value">
<textarea v-else type="text" v-model="title.value"></textarea>

Related

vuetify datatable search cannot search the value

I found the miss function on vuetify datatable.
I think that my configuration is correct.
This is my datatable vuetify
<v-text-field v-model="search" append-icon="mdi-magnify" label="Search" single-line
hide-details></v-text-field>
</v-card-title>
<v-data-table loading loading-text="Loading... Please wait" :headers="headers"
:items="goodsData" :search="search" :items-per-page="10" class="elevation-1">
<template v-slot:item.typeOfGoods="{ item }">
<div class="userDatatable-inline-title my-3">
<a href="#" class="text-dark fw-500">
<h6 v-if="item.typeOfGoods == 1">Document
#{{item.receiptNumber}}</h6>
<h6 v-if="item.typeOfGoods == 2">Packet
#{{item.receiptNumber}}</h6>
</a>
<p class="pt-1 d-block mb-0">
<i class="fas fa-dolly"></i> via {{item.courier}} • Received at
{{item.created_at}}
</p>
</div>
</template>
<template v-slot:item.receiver="{ item }">
<img class="rounded-circle wh-34"
:src="`/dashboard/img/author/profile/`+item.receiver.avatar"
alt="author">
{{item.receiver.name}}
</template>
<template v-slot:item.status="{ item }">
<span class="media-badge color-white bg-primary" v-if="item.status==0">Available
at
receiptionist</span>
<span class="media-badge color-white bg-success" v-if="item.status==1">Well
received</span>
<span class="media-badge color-white bg-danger"
v-if="item.status==2">Terminated</span>
</template>
<template v-slot:item.actions="{item}">
<a href="#" v-on:click="receivedAction(item.id)" v-if="item.status==0"><span
style="color:blue;"><i class="far fa-check-circle"></i> Update
status</span></a>
<a v-if="item.status==1"><span style="color:green;"><i
class="fas fa-check-circle"></i>
Done</span></a>
</template>
</v-data-table>
This is my script data
// datatable
search: '',
goodsData: [],
headers: [{
text: 'Goods Type',
value: 'typeOfGoods'
}, {
text: 'Recipient',
value: 'receiver'
}, {
text: 'Status',
value: 'status'
}, {
text: 'Actions',
value: 'actions',
filterable: false,
sortable: false
}],
I already read the documentation on codepen and on https://vuetifyjs.com/en/components/data-tables/#api
Or you can see my repo,
https://github.com/bintangjtobing/boxity-app/blob/master/resources/js/components/goodsReceipt.vue
You can see my gif below
Thank you, everyone, hope you can help to solve this prob.
Thank you thank you.
The search only looks in fields you’ve defined. The text “Document” is not in a field, it’s in your template. The number comes from receiptNumber which is not defined in headers or anywhere, it’s just in the template. Therefore the search can’t know about that either.
You’ll need to provide your own filter function as the documentation you linked to explains in the beginning. I don’t see such defined here.
I found a simple solution for this by creating same header and value but hidden for the column used in template.
You can try this:
{ text: "", value: "header_value", align: ' d-none' },
{ text: "Header Name", value: "header_value" }
Wherein you have to hide the other one by using ' d-none' that will be use to still search the value of that column while the other one is use to display the value. Else if you don't want to use this, you can use the other solution stated here, by creating a custom filter/search.

Vue bootstrap form tags allowing duplicates

I'm trying to allow duplicates in Vue bootstrap's form tags.
I've tried using :tag-validator (in the example) and #tag-state to externally modify the v-model value. However it seems like it is getting rid of the duplicate somewhere. Is this impossible?
Jsfiddle of the example: https://jsfiddle.net/yts54fpd/.
<div id="app">
<template>
<div>
<b-form-group label="Tagged input using select" label-for="tags-component-select">
<!-- Prop `add-on-change` is needed to enable adding tags vie the `change` event -->
<b-form-tags
:tag-validator="tagValidator"
id="tags-component-select"
v-model="value"
size="lg"
class="mb-2"
add-on-change
no-outer-focus
>
<template v-slot="{ tags, inputAttrs, inputHandlers, disabled, removeTag }">
<ul v-if="tags.length > 0" class="list-inline d-inline-block mb-2">
<li v-for="tag in tags" :key="tag" class="list-inline-item">
<b-form-tag
#remove="removeTag(tag)"
:title="tag"
:disabled="disabled"
variant="info"
>{{ tag }}</b-form-tag>
</li>
</ul>
<b-form-select
v-bind="inputAttrs"
v-on="inputHandlers"
:disabled="disabled"
:options="options"
>
<template #first>
<!-- This is required to prevent bugs with Safari -->
<option disabled value="">Choose a tag...</option>
</template>
</b-form-select>
</template>
</b-form-tags>
</b-form-group>
</div>
</template>
</div>
window.onload = () => {
new Vue({
el: '#app',
data() {
return {
options: ['Apple', 'Orange', 'Banana', 'Lime', 'Peach', 'Chocolate', 'Strawberry'],
value: []
}
},
methods: {
tagValidator(tag) {
this.value.push(tag)
return true
}
}
})
}

Vuejs/Buetify : clear search filter using searchable prop of b-table

I'm using buefy to create a table with input filters in columns.
This is what it looks like:
<b-table
:data="cars"
:sticky-header="true"
:selected.sync="selected"
>
<template slot-scope="props">
<template v-for="column in columns">
<b-table-column :key="column.id" v-bind="column">
<template
v-if="column.searchable"
slot="searchable"
slot-scope="props"
>
<b-input
v-model="props.filters[props.column.field]"
placeholder="Search..."
icon="magnify"
size="is-small"
/>
</template>
{{ props.row[column.field] }}
</b-table-column>
</template>
</template>
</b-table>
...
...
data () {
return {
selected: null,
columns: [
{
field: 'constructor',
label: 'Constructor',
searchable: true
},
....
]
}
I would like to be able to clear the searched fields.
Any suggestions to achieve this please?
Your b-input is bound to props.filters[props.column.field], so it means that you should be able to add an icon to reset this value:
<b-input
v-model="props.filters[props.column.field]"
...
icon-right="close-circle"
icon-right-clickable
#icon-right-click="props.filters[props.column.field] = ''"
>
Please let me know if that works.

How can I get value in datetimepicker bootstrap on vue component?

My view blade, you can see this below :
...
<div class="panel-body">
<order-view v-cloak>
<input slot="from-date" data-date-format="DD-MM-YYYY" title="DD-MM-YYYY" type="text" class="form-control" placeholder="Date" name="from_date" id="datetimepicker" required>
<input slot="to-date" data-date-format="DD-MM-YYYY" title="DD-MM-YYYY" type="text" class="form-control" placeholder="Date" name="to_date" id="datetimepicker" required>
</order-view>
</div>
...
My order-view component, you can see this below :
<template>
<div>
<div class="col-sm-2">
<div class="form-group">
<slot name="from-date" required v-model="fromDate"></slot>
</div>
</div>
<div class="col-sm-1">
<div class="form-group" style="text-align: center">
-
</div>
</div>
<div class="col-sm-2">
<div class="form-group">
<slot name="to-date" required v-model="toDate"></slot>
</div>
</div>
<div class="col-sm-4">
<button v-on:click="filter()" class="btn btn-default" type="button">
<span class="glyphicon glyphicon-search"></span>
</button>
</div>
</div>
</template>
<script>
export default {
data() {
return{
fromDate: '',
toDate: ''
}
},
methods: {
filter: function() {
console.log(this.fromDate)
console.log(this.toDate)
}
}
}
</script>
I using v-model like above code
But, when I click the button, the result of
console.log(this.fromDate)
console.log(this.toDate)
is empty
It display empty
Why it does not work?
How can I solve it?
You cannot bind a slot using v-model and expect that Vue will attach that automatically to your slot input, but I can't see any reason why you need to use a slot here anyway. It looks like you just want an input that you can attach custom attributes to and you can do that by passing the attributes as a prop and use v-bind to bind them:
<template>
<div>
<input v-bind="attrs" v-model="fromDate" />
<button #click="filter">filter</button>
</div>
</template>
export default{
props: ['attrs'],
methods: {
filter() {
console.log(this.fromDate)
}
},
data() {
return {
fromDate: ""
}
}
}
new Vue({
el: "#app",
data: {
fromDateAttrs: {
'data-date-format': "DD-MM-YYYY",
title: "DD-MM-YYYY",
type: "text",
class: "form-control",
placeholder: "Date",
name: "from_date",
id: "datetimepicker",
}
}
});
Now you can just pass your attrs as a prop in the parent:
<my-comp :attrs="fromDateAttrs"></my-comp>
Here's the JSFiddle: https://jsfiddle.net/rvederzc/
EDIT
In reference as to how to create a date picker component, here's how I would implement a jQuery datepicker using Vue.js:
<template id="date-picker">
<div>
<input v-bind="attrs" v-model="date" #input="$emit('input', $event.target.value)" v-date-picker/>
</div>
</template>
<script type="text/javascript">
export default {
props: ['attrs'],
directives: {
datePicker: {
bind(el, binding, vnode) {
$(el).datepicker({
onSelect: function(val) {
// directive talk for 'this.$emit'
vnode.context.$emit('input', val);
}
});
}
}
}
}
</script>
You can then bind that with v-model in the parent:
<date-picker v-model="myDate"></date-picker>
Here's the JSFiddle: https://jsfiddle.net/g64drpg6/
Cant expect any javascript technology to be complete before it becomes famous. Going by that, I tried all the recommendations from using moment to vue-datapicker. All recommendations heavily broke design and needed hardcode of the div id's in the vue initialisation under mounted. Cant introduce hacks into my project this way. Messes up design and implementation neatness.
I fixed it using plain old jsp. On Save, I just did this
vuedata.dateOfBirthMilliSecs = $("#dateOfBirth").val() ;
I'll figure out conversion of date format to milliseconds in my java controller.

Vue JS – Using v-if with components

I'm using VueJS components to create a dynamic pricing table. One of the more 'static' elements is a 'most popular' label which is added to the Team plan. I want to be able to use v-if to display a and add an extra class on the plan marked as most popular. I've simplified the code for brevity.
You can see I have tried multiple ways of formatting the expression (currently differs between the v-bind and the v-if) but I'm not sure if this approach is even possible.
Here is the HTML.
<div id="app">
<ul class="plans">
<plan-component :
name="Basic"
most-popular=false
></plan-component>
<plan-component :
name="Recreational"
most-popular=false
></plan-component>
<plan-component :
name="Team"
most-popular=true
></plan-component>
<plan-component :
name="Club"
most-popular=false
></plan-component>
</ul>
<template id="plan-component">
<li v-bind:class="{ 'most-popular': mostPopular == true }">
<template v-if="most-popular === true">
<span class="popular-plan-label">Most popular</span>
</template>
<p>{{ name }}</p>
</li>
</template>
</div>
And here is the JS.
Vue.component('plan-component', {
template: '#plan-component',
props: ['name', 'mostPopular'],
});
new Vue({
el: '#app';
});
You need to validate the mostPopular property type, since it's Boolean it won't work when you place most-popular=true because it's considered as a string "true" not true instead put most-popular tag on popular plan only. Here is example:
Vue.component('plan-component', {
template: '#plan-component',
props: {
name: String,
mostPopular: {
type: Boolean,
default: false,
}
},
});
new Vue({
el: '#app'
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/1.0.25/vue.min.js"></script>
<div id="app">
<ul class="plans">
<plan-component name="Basic"></plan-component>
<plan-component name="Recreational"></plan-component>
<plan-component name="Team" most-popular></plan-component>
<plan-component name="Club"></plan-component>
</ul>
</div>
<template id="plan-component">
<li v-bind:class="{ 'most-popular': mostPopular }">
<p>{{ name }} <small v-if="mostPopular" class="popular-plan-label" style="color: red">Most popular</small></p>
</li>
</template>
Use <template v-if="ok"> for conditional rendering.