Passing dynamic object values to Modal Popup in VUE opening wrong ID value - vue.js

I'm passing a dynamic object value into a Vue modal template but for some reason the ID that gets passed is always 1 + the selected ID, even though my console.log shows the right ID selected. It should be opening content for the ID selected.
(Also my modal button isnt closing.)
My pen is here: You can see its always trying to open the id + 1
https://codepen.io/omarel/pen/jXJVPw
VUE
// global component
Vue.component('popup',{
template: '#popup',
props: ["floorplan"]
})
//VUE connection
var floorplans = new Vue({
el:"#floorplans",
data: {
popup: false,
id: 1,
floorplans: [
{
"id": 1,
"building": "214",
"residence": "106",
"classname": "studio",
"bed": 0,
"bath": 1,
"den": 0,
"price": "$x,xxx",
"img": "floorplans/images/x.png",
"pdf": "floorplans/pdfs/x.pdf"
},
{
"id": 2,
"building": "214",
"residence": "109",
"classname": "1bed",
"bed": 1,
"bath": 1,
"den": 0,
"price": "$x,xxx",
"img": "floorplans/images/x.png",
"pdf": "floorplans/pdfs/x.pdf"
},
{
"id": 3,
"building": "214",
"residence": "208",
"classname": "1bed",
"bed": 1,
"bath": 1,
"den": 0,
"price": "$x,xxx",
"img": "floorplans/images/x.png",
"pdf": "floorplans/pdfs/x.pdf"
},
{
"id": 4,
"building": "214",
"residence": "205",
"classname": "1bed",
"bed": 1,
"bath": 1,
"den": 1,
"price": "$x,xxx",
"img": "floorplans/images/x.png",
"pdf": "floorplans/pdfs/x.pdf"
},
{
"id": 5,
"building": "210",
"residence": "303",
"classname": "2bed",
"bed": 2,
"bath": 2,
"den": 0,
"price": "$x,xxx",
"img": "floorplans/images/x.png",
"pdf": "floorplans/pdfs/x.pdf"
}
]
},
methods: {
// opennfloorplan: function(event) {
// console.log(event.id);
// }
pop: function(id){
console.log(id);
this.id = id;
console.log(this.id);
this.popup = true;
}
}
})
HTML
<section id="floorplans" class="table">
<table v-cloak class="sortable">
<thead>
<tr>
<th scope="col" class="sorttable_sorted">Residence<span id="sorttable_sortfwdind"> ▾</span></th>
<th scope="col">Bed/Bath</th>
<th scope="col">Building</th>
<th scope="col">Price</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
<tr v-for="floorplan in floorplans" v-bind:class="floorplan.classname">
<td data-label="Residence">{{ floorplan.residence }}</td>
<td data-label="Bed/Bath">
<span v-if="floorplan.bed"> {{floorplan.bed}} BEDROOM </span>
<span v-else="floorplan.bed"> STUDIO </span>
<span v-if="floorplan.den"> + {{floorplan.den}} DEN </span>
<span v-if="floorplan.bath"> / {{floorplan.bath}} BATH</span>
</td>
<td data-label="Building">{{ floorplan.building }}</td>
<td data-label="Price">{{ floorplan.price }}</td>
<td data-label="Floor Plan">
{{ floorplan.id }}
<a v-on:click="pop(floorplan.id)" href="javascript:;" class="btn view white openfloorplan">View</a>
<a v-bind:href="floorplan.pdf" target="_blank" class="btn apply blue">Apply</a>
</td>
</tr>
</tbody>
</table>
<popup v-if="popup" :floorplan="floorplans[id]"></popup>
</section>
<template id="popup">
<transition name="popup">
<div class="popup">
<div class="content"><img width="200" height="106" />
<p>{{ floorplan.id }}</p>
<p>{{ floorplan.residence }}</p>
<button v-on:click="floorplans.$data.popup = false">button</button>
</div>
</div>
</transition>
</template>

You are using the index, not the id. floorplans[id] is just an array index. Your ids number from 1, but arrays number from zero, so floorplans[1] is the second floorplan, whose id is 2.

floorplans[id] will get the floorplan at INDEX id. Your floorplan ids start at 1 and array indexes start at 0.
<tr v-for="(floorplan, index) in floorplans" v-bind:class="floorplan.classname">
<a v-on:click="pop(index)" href="javascript:;" class="btn view white openfloorplan">View</a>

Related

Multiple select inputs in table header with unique models

I am receiving data from the backend that takes the following format
[
[
[ "123", "21/11/2013", "Data", "Data" ],
[ "234", "22/11/2013", "Data", "Data" ],
[ "345", "12/09/2018", "Data", "Data" ],
],
[
[ "123", "Data", "Data", "Data", "Data", "Data", "Data", "Data", "Data", "Data", "Data" ],
[ "234", "Data", "Data", "Data", "Data", "Data", "Data", "Data", "Data", "Data", "Data" ],
[ "345", "Data", "Data", "Data", "Data", "Data", "Data", "Data", "Data", "Data", "Data" ]
]
]
Each fileData represents a table, so in the above example it should produce two tables. The data inside contains a tables rows, so each table above has two rows.
In order to achieve this I am doing something like the following.
<table class="table" v-for="(file, index) in fileData" :key="index">
<tbody>
<tr v-for="(row, index2) in file":key="index2">
<td v-for="(data, index3) in row" :key="index3">
{{ data }}
</td>
</tr>
</tbody>
</table>
This all seems to work fine. However, the data I am using does not have headers, but I need to prodive a header for each column that contains a select. As such I have added the following
<table class="table" v-for="(file, index) in fileData" :key="index">
<thead>
<tr>
<th scope="col" v-for="(col, index2) in file[index]" :key="index2">
<b-form-select v-model="options.value" :options="options"></b-form-select>
</th>
</tr>
</thead>
</table>
Once again this seems to work. My problem is that I want the user to define what a column represents, using the select. At the moment, if I select something, they all change.
I have produced this Fiddle as an example https://jsfiddle.net/mhyv62bt/1/
How can I make the selects independent, and is it also possible to remove an option once selected?
Thanks
This seems to produce the correct number of header columns for each table.
Update
I have a slightly different setup so trying to fit it in with my project. As such, I created the file THeadSelect.vue
<template id="theadselect">
<thead>
<tr>
<th
v-for="(i,index) in this.length_"
:key="index"
>
<select
v-model="headers[index]">
<option disabled value="">
Please select one
</option>
<option
v-if="headers[index]"
selected
>
{{headers[index]}}
</option>
<option
v-for="option in filteredOptions"
:key="option"
>
{{option}}
</option>
</select>
</th>
</tr>
</thead>
</template>
<script>
export default {
mounted () {
this.$emit('update:headers',
this.headers
.concat(Array.from({ length: this.length_ }, _ => ''))
.slice()
)
},
props: {
options: {
type: Array,
required: true
},
length: Number,
headers: {
type: Array,
required: true
}
},
computed: {
length_: {
get () {
return this.length || this.options.length
},
set (l) {
this.$emit('update:length', l)
}
},
filteredOptions () {
return this.options.filter(
option => !this.headers.includes(option)
)
}
}
}
</script>
I am then trying to use this within my page
<template>
<div>
<b-form
novalidate
#submit.stop.prevent=""
>
<div class="row">
<div class="col-12">
<table class="table table-bordered" v-for="(file, index) in fileData" :key="index">
<thead
is="THeadSelect"
:options="['option1', 'option2', 'option3']"
:headers.sync="headers"
></thead>
<tbody>
<tr v-for="(row, index2) in file" :key="index2">
<td v-for="(data, index3) in row" :key="index3">
{{ data }}
</td>
</tr>
</tbody>
</table>
</div>
</div>
</b-form>
</div>
</template>
<script>
import { THeadSelect } from '#/components/common/THeadSelect'
export default {
components: {
THeadSelect
},
computed: {
fileData () {
return this.$store.getters.fileData
}
},
data () {
return {
headers: [],
length: 10,
}
}
}
</script>
It is a bit messed up though. Only 3 selects are being displayed for each table. Additionally, if I select an option in table 1, it selects the same option in table 2. If you check out my original fiddle you can see the initial data I am trying to work with, so there will always be two tables.
<div id='app'>
<table class="table table-bordered" v-for="(file, index) in fileData" :key="index">
<thead>
<tr>
<th scope="col" v-for="(col, index2) in file[index]" :key="index2+index">
<b-form-select v-model="selectedValue[index+index2]" :options="options">
<template v-slot:first>
<b-form-select-option :value="null" disabled>Ignore</b-form-select-option>
</template>
</b-form-select>
</th>
</tr>
</thead>
<tbody>
<tr v-for="(row, index2) in file" :key="index2">
<td v-for="(data, index3) in row" :key="index3">
{{ data }}
</td>
</tr>
</tbody>
data: {
selectedValue: [],
mappedColumns: [],
selected: null,
options: [{
value: '1',
text: 'Option 1'
},
{
value: '2',
text: 'Option 2'
},
{
value: '3',
text: 'Option 3'
}
],
You are using a single value in v-model for all the dropdowns. so, once you change a single dropdown. All of them gets changed.
Try the above solution, where I declared a new array in data which is selectedValue
You can keep the data of which dropdown is selected in this array
I guess you can make this a component
The header:
Possible usage:
<thead
is="THeadSelect"
:options="header_row"
:length="length /*defaults to options.length*/"
:headers.sync="headers"
></thead>
The ComponentOptions
//*.js
const THeadSelect = {
template: "#theadselect",
mounted(){
// make sure headers is populated
this.$emit("update:headers",
this.headers
.concat(Array.from({length:this.length_}, _=>""))
.slice()
)
},
props: {
options: {
type: Array,
required: true,
},
length: Number,
headers: {
type: Array,
required: true
}
},
computed:{
length_:{
get(){
return this.length || this.options.length
},
set(l){
this.$emit("update:length",l)
}
},
filteredOptions(){
return this.options.filter(
option => !this.headers.includes(option)
)
}
}
}
The template
// *.html
<template id="theadselect">
<thead>
<tr>
<th
v-for="(i,index) in length_"
:key="index"
>
<select
v-model="headers[index]">
<option disabled value="">
Please select one
</option>
<option
v-if="headers[index]"
selected
>
{{headers[index]}}
</option>
<option
v-for="option in filteredOptions"
:key="option"
>
{{option}}
</option>
</select>
</th>
</tr>
</thead>
</template>
Example
const THeadSelect = {
template: "#theadselect",
mounted(){
// make sure headers is populated
this.$emit("update:headers",
this.headers
.concat(Array.from({length:this.length_}, _=>""))
.slice()
)
},
props: {
options: {
type: Array,
required: true,
},
length: Number,
headers: {
type: Array,
required: true
}
},
computed:{
length_:{
get(){
return this.length || this.options.length
},
set(l){
this.$emit("update:length",l)
}
},
filteredOptions(){
return this.options.filter(
option => !this.headers.includes(option)
)
}
}
}
new Vue({
components: {THeadSelect},
data(){
return {
headers: [],
length: 10
}
},
template: "#root"
}).$mount('#app')
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<template id="theadselect">
<thead>
<tr>
<th
v-for="(i,index) in length_"
:key="index"
>
<select
v-model="headers[index]">
<option value="">
Please select one
</option>
<option
v-if="headers[index]"
selected
>
{{headers[index]}}
</option>
<option
v-for="option in filteredOptions"
:key="option"
>
{{option}}
</option>
</select>
</th>
</tr>
</thead>
</template>
<template id="root">
<div>
<table>
<caption>Sample usage with select</caption>
<thead
is="THeadSelect"
:options="['option1', 'option2', 'option3']"
:headers.sync="headers"
></thead>
<tbody>
<tr>
<td
v-for="(prop, index) in headers"
:key="prop+index"
>
{{ prop || '?'}}
</td>
</tr>
</tbody>
</table>
</div>
</template>
<table id="app"></table>
For the body one can think about the value of the headers array. Maybe put array indeces or object properties instead of currently option values
So for multiple tables one can think about:
<template id="table">
<table
v-for="(table, index) in tables"
:key="'table-'+index"
is="TableSelect"
:headers="table[0]"
:rows="table.slice(1)"
>
</table>
</template>
And for TableSelect:
const TableSelect = {
props: ["headers", "rows"],
template: "#table-select",
data(){
return {
selectedHeaders: []
}
},
computed(){
mappedRows(){
return this.rows
.map(row=> row.map(
(cell, index) => ({[headers[index]]: cell})
).reduce((obj, val) => Object.assign(obj, val))
)}
}
}
<template id="table-select">
<table>
<thead
is="THeadSelect"
:options="headers"
:headers.sync="selectedHeaders"
></thead>
<tbody>
<tr
v-for="(row, index) in mappedRows"
:key="'row-'+index"
>
<td
v-for="cell in selectedHeaders"
:key="cell+index"
>
{{cell && row[cell || ""]}}
</td>
</tr>
</tbody>
</table>
</template>
there are errors in above code but due to lazyness and missing linter on so i will let it be - as it provides the basic idea.
And a running example on codesandbox:
https://lbm8l.csb.app/
Use v-on:change and a function instead of v-model Here is the solution for individual selection
new Vue({
el: "#app",
data: {
mappedColumns: [],
selected: null,
options: [{
value: '1',
text: 'Option 1'
},
{
value: '2',
text: 'Option 2'
},
{
value: '3',
text: 'Option 3'
}
],
fileData: [
[
["123", "21/11/2013", "Data", "Data"],
["234", "22/11/2013", "Data", "Data"],
["345", "12/09/2018", "Data", "Data"],
],
[
["123", "Data", "Data", "Data", "Data", "Data", "Data", "Data", "Data", "Data",
"Data"
],
["234", "Data", "Data", "Data", "Data", "Data", "Data", "Data", "Data", "Data",
"Data"
],
["345", "Data", "Data", "Data", "Data", "Data", "Data", "Data", "Data", "Data",
"Data"
]
]
]
},
methods: {
getSelectedItem(a, b, c) {
console.log(a, b, c);
}
}
})
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://unpkg.com/tether#1.4.7/dist/css/tether.min.css">
<link rel="stylesheet" href="https://unpkg.com/bootstrap-vue#2.15.0/dist/bootstrap-vue.css">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css">
<script src="https://unpkg.com/vue#2.6.11/dist/vue.js"></script>
<script src="https://unpkg.com/tether#1.4.7/dist/js/tether.min.js"></script>
<script src="https://unpkg.com/bootstrap-vue#2.15.0/dist/bootstrap-vue.js"></script>
<title>Document</title>
<style>
#app {
padding: 20px;
height: 500px;
}
</style>
</head>
<body>
<div id='app'>
<table class="table table-bordered" v-for="(file, index) in fileData" :key="index">
<thead>
<tr>
<th scope="col" v-for="(col, index2) in file[index]" :key="index2">
<b-form-select v-on:change="getSelectedItem($event,index,index2)" :options="options">
<template v-slot:first>
<b-form-select-option :value="null" disabled>Ignore</b-form-select-option>
</template>
</b-form-select>
</th>
</tr>
</thead>
<tbody>
<tr v-for="(row, index2) in file" :key="index2">
<td v-for="(data, index3) in row" :key="index3">
{{ data }}
</td>
</tr>
</tbody>
</table>
</div>
<script src="table.js"></script>
</body>
</html>

VueJS with Bootstrap icons for sort

I'm sorting column data by click on the table header.
I have added the Bootstrap icons based on the v-show condition(s).
Earlier, the sorting worked on the click of the table header but now it should work on the click of icon which are not visible now to the right of 'Present' column.
Need help on this. The origin of issue is on Line 13 in HTML:
<span class="glyphicon glyphicon-sort" v-show="toolAttribute != activeColumn"></span>
https://jsfiddle.net/L5p0ngdu/2/
new Vue({
el: '#app',
data: {
results: {
toolAttribute: [{attributeName: "Present", order: 1},{attributeName: "Present", order: 1},{attributeName: "Present", order: 1}],
device: [
{deviceName: "Device Name 1",
info: [{value: true}, {value: false}, {value: true}]},
{deviceName: "Device Name 2",
info: [{value: false}, {value: false}, {value: false}]},
{deviceName: "Device Name 3",
info: [{value: true}, {value: true}, {value: true}]},
{deviceName: "Device Name 4",
info: [{value: false}, {value: true}, {value: false}]},
{deviceName: "Device Name 5",
info: [{value: true}, {value: true}, {value: true}]}
]
},
activeColumn: {},
currentSort:['deviceName'],
currentSortDir:'asc'
},
computed:{
sortedResults:function() {
return this.results.device.sort(function(a,b){
let modifier = 1;
if(this.currentSortDir === 'desc') modifier = -1;
this.currentSort.forEach(x => {
a = a[x];
b = b[x];
})
if(a< b) return -1 * modifier;
if(a > b) return 1 * modifier;
return 0;
}.bind(this));
}
},
methods:{
flasecond(index){
let res = false
this.results.device[index].info.forEach(info=> {
if(!info.value) res = true
})
return res
},
sort:function(s) {
//if s == current sort, reverse
if(s.join('') === this.currentSort.join('')) {
this.currentSortDir = this.currentSortDir==='asc'?'desc':'asc';
}
this.currentSort = s;
},
}
})
.falseclass{
background:red;
color:white;
}
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
<div id="app">
<table >
<tr>
<th rowspan="2" #click="sort(['deviceName'])">Device Info</th>
</tr>
<tr>
<th v-for="(toolAttribute, index) in results.toolAttribute" :key="index" #click="activeColumn = toolAttribute" :class="{active: toolAttribute == activeColumn}">{{toolAttribute.attributeName}}
<span #click="sort(['info', index, 'value']); toolAttribute.order = toolAttribute.order * (-1)" :class="toolAttribute.order > 0 ? 'glyphicon glyphicon-chevron-down' : 'glyphicon glyphicon-chevron-up'" v-show="toolAttribute == activeColumn"></span>
<span class="glyphicon glyphicon-sort" v-show="toolAttribute != activeColumn"></span></th>
</tr>
<tr v-for="(device, index) in sortedResults" >
<td :class="{'falseclass' : flasecond(index)}">{{ device.deviceName }}</td>
<td v-for="info in device.info" :class="{'falseclass' : !info.value}">{{info.value}}</td>
</tr>
</table>
</div>
In your jsfiddle I see that you are using Bootstrap 4. When Bootstrap migrated to v4 the Glyphicons icon font has been dropped. I'd suggest you to take a look at one of these free alternatives:
FontAwesome
Octicons
You could try this
i have made some changes to jsfiddle https://jsfiddle.net/thanseeh/tqy93meL/13/
<head>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>
<body>
<div id="app">
<table >
<tr>
<th rowspan="2" #click="sort(['deviceName'])">Device Info</th>
</tr>
<tr>
<th v-for="(toolAttribute, index) in results.toolAttribute" :key="index" #click="activeColumn = toolAttribute" :class="{active: toolAttribute == activeColumn}">{{toolAttribute.attributeName}}
<span #click="sort(['info', index, 'value']); toolAttribute.order = toolAttribute.order * (-1)" :class="toolAttribute.order > 0 ? 'glyphicon glyphicon-chevron-down' : 'glyphicon glyphicon-chevron-up'" v-show="toolAttribute == activeColumn"></span>
<span class="glyphicon glyphicon-sort" v-show="toolAttribute != activeColumn"></span></th>
</tr>
<tr v-for="(device, index) in sortedResults" >
<td :class="{'falseclass' : flasecond(index)}">{{ device.deviceName }}</td>
<td v-for="info in device.info" :class="{'falseclass' : !info.value}">{{info.value}}</td>
</tr>
</table>
</div>
</body>

Vue js showing 2 table value

My array like:
[
{
"id": 01,
"no": "ABC",
"offer": [
{
"offer_no": "ABC_01",
},
{
"offer_no": "ABC_02",
},
{
"offer_no": "ABC_05",
}
]
},
{
"id": 02,
"no": "EFG",
"offer": [
{
"offer_no": "EFG_01",
},
]
}
]
Here i want to show:
no--------------offer
ABC    ABC_01
      ABC_02
      ABC_05
EFG    EFG_01
how can i show that in vue js?
i try before:
<table>
<tbody>
<template v-for="item in array_list">
<template v-for="offer in item.offer">
<tr>
<th>{{item.no}}</th>
<th>{{offer.offer_no}}</th>
</tr>
</template>
</template>
</tbody>
</table>
no--------------offer
ABC    ABC_01
ABC    ABC_02
ABC    ABC_05
EFG    EFG_01
but the result not my preferred
Try the following code:
<table>
<tbody>
<template v-for="item in array_list">
<template v-for="(offer,index) in item.offer">
<tr>
<td><div v-show = "index ==0">{{item.no}}</div></td>
<td>{{offer.offer_no}}</td>
</tr>
</template>
</template>
</tbody>
</table>
One way would be to use the index and only show the ABC/EFG-item when the index equals 0.

Reusing a form with Vue.js

I have a page with a list of things. I want the user to be able to click on an item in the list and open a modal dialog with a relatively complicated form with data for each item. I've implemented the form using Vue.js, and so far, it works, but I can't figure out how to get the Vue to switch from the data for one item to the data for another item, or do something equivalent to that.
The examples make it look like components are for small things and not entire forms. Here's the JavaScript from a JS fiddle with a hastily-coded example that's kind of similar to what I want to do.
HTML:
<table>
<thead>
<tr>
<th scope="col">Item</th>
<th scope="col">Data</th>
</tr>
</thead>
<tbody>
<tr data-identifier="1">
<td><a>Item 1</a></td>
<td class="json">{ "id": "1", "formData": { "whichOption": "1", "optionOneSetting": "ONE AYE", "optionTwoSetting": null, "optionThreeSetting": null } }</td>
</tr>
<tr data-identifier="2">
<td><a>Item 2</a></td>
<td class="json">{ "id": "2", "formData": { "whichOption": "2", "optionOneSetting": null, optionTwoSetting": "two-b", "optionThreeSetting": null } }</td>
</tr>
<tr data-identifier="3">
<td><a>Item 3</a></td>
<td class="json">{ "id": "3", "formData": { "whichOption": "2", "optionOneSetting": null, "optionTwoSetting": "two-c", "optionThreeSetting": null } }</td>
</tr>
<tr data-identifier="4">
<td><a>Item 4</a></td>
<td class="json">{ "id": "4", "formData": { "whichOption": "3", "optionOneSetting": null, "optionTwoSetting": null, "optionThreeSetting": "true" } }</td>
</tr>
<tr data-identifier="5">
<td><a>Item 5</a></td>
<td class="json">{ "id": "5", "formData": { "whichOption": "1", "optionOneSetting": "ONE AYE AGAIN", "optionThreeSetting": null } }</td>
</tr>
</tbody>
</table>
<div id="app" class="hidden">
<select v-model="formData.whichOption">
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
</select>
<label v-if="formData.whichOption==1">1-A: <input type="text" v-model="formData.optionOneSetting" /></label>
<div v-if="formData.whichOption==2">
<label><input type="radio" name="optionTwoSetting" value="two-a" v-model="formData.optionTwoSetting" /> 2-A</label>
<label><input type="radio" name="optionTwoSetting" value="two-b" v-model="formData.optionTwoSetting" /> 2-B</label>
<label><input type="radio" name="optionTwoSetting" value="two-c" v-model="formData.optionTwoSetting" /> 2-C</label>
</div>
<div v-if="formData.whichOption==3">
<label><input type="checkbox" name="optionThreeSetting" value="three" v-model="formData.optionThreeSetting" /> 3-A</label>
</div>
<button v-on:click="doMagic">Do a Thing</button>
</div>
JavaScript:
$(document).ready(function() {
$("a").click(function() {
var data = $(this).closest("tr").find("td.json").text();
data = JSON.parse(data);
if (!document.vue) {
document.vue = new Vue({
el: "#app"
, data: data
, methods: {
doMagic: function(event) {
var $tr = $("tr[data-identifier=" + this.id + "]");
console.log($.extend({}, this));
$tr.find(".json").text(JSON.stringify({
"id": this.id
, "formData": this.formData
}));
}
}
})
$("#app").removeClass("hidden");
}
else {
// This isn't expected to work, but it's effectively what I'd like to happen.
document.vue.data = data;
}
})
})
https://jsfiddle.net/don01001100/eywraw8t/370396/

yadcf - datatable with ajax serverSide and external filter

I develop an application and I used my DataTable and Yadcf.
I have lots of data in my database, so I am used to search in ajax.
I need to filter above the table but now the search is done only on the first page of the table. How to do a search on the whole picture?
My code :
JS
var oTable;
oTable = $('#table-announcement').DataTable({
"processing": true,
"serverSide": true,
"bServerSide": true,
"responsive": true,
"stateSave": true,
"autoWidth": false,
"bJQueryUI": true,
"bStateSave": true,
//"ajax": "/app_dev.php/admin/avantages/announcement/datatable/add/ajax",
ajax: {
url: "/app_dev.php/admin/avantages/announcement/datatable/add/ajax",
type: "POST"
},
"sAjaxDataProp": "data",
"pageLength": 10,
"paging": true,
"searching": true,
"bFilter": true,
"order": [[ 1, 'desc' ]],
"columnDefs": [{
"targets": 'no-sort', // no sort cols
"orderable": false
}],
"language": {
"lengthMenu": $lengthMenu,
"zeroRecords": $zeroRecords,
"info": $info,
"infoEmpty": $infoEmpty,
"infoFiltered": $infoFiltered,
"searchPlaceholder": $searchPlaceholder,
"search":$search,
"sProcessing": $sProcessing,
"oPaginate": {
"sFirst": $sFirst,
"sLast": $sLast,
"sNext": $sNext,
"sPrevious": $sPrevious
}
},
"columns":[
{"data": "announcement"},
{"data": "category"},
{"data": "from"},
{"data": "created_at"},
{"data": "validationDate"},
{"data": "priority"},
{"data": "remainingValidate"},
{"data": "status"},
{"data": "nbcontacts"},
{"data": "htmlActions"}
]
});
yadcf.init(oTable, [
{column_number : 0,
filter_type: "text",
filter_container_id: 'external_filter_title',
filter_default_label: "Titre"
},
{ column_number : 1,
select_type: 'select2',
filter_container_id: 'external_filter_category',
filter_default_label: "Rubrique"
},
{column_number : 2,
filter_type: "text",
filter_container_id: 'external_filter_user_announcement',
filter_default_label: "De"
},
{column_number : 5,
select_type: 'select2',
filter_container_id: 'external_filter_priority',
filter_default_label: "Urgence"
},
{column_number : 7,
select_type: 'select2',
column_data_type: "html",
html_data_type: "text",
filter_container_id: 'external_filter_status',
filter_default_label: "Etat"
}
],{ externally_triggered: true});
Html
<div class="filter-block tiny-full">
<div class="row">
<div class="col-xs-12 tiny-full list-count">
<div>
<span><b>{{ nbannouncement }}</b></span> {{ 'message.linkingAnnoucement.list.label_mer1'|trans }} <span><b>{{ nbAnnouncementsPending }}</b></span> {{ 'message.linkingAnnoucement.list.label_mer2'|trans }}
</div>
<div>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12 filter-content">
<div class="filter-table">
<div class="filter-table-cell">
<div class="label_filter">{{ 'message.linkingAnnoucement.list.filter.filter_announcement'|trans }} </div><div class="filter"><span id="external_filter_title"></span></div>
</div>
<div class="filter-table-cell">
<div class="label_filter">{{ 'message.linkingAnnoucement.list.filter.filter_category'|trans }} </div><div class="filter"><span id="external_filter_category"></span></div>
</div>
<div class="filter-table-cell">
<div class="label_filter">{{ 'message.linkingAnnoucement.list.filter.filter_from'|trans }} </div><div class="filter"><span id="external_filter_user_announcement"></span></div>
</div>
<div class="filter-table-cell">
<div class="label_filter">{{ 'message.linkingAnnoucement.list.filter.filter_priority'|trans }} </div><div class="filter"><span id="external_filter_priority"></span></div>
</div>
<div class="filter-table-cell">
<div class="label_filter">{{ 'message.linkingAnnoucement.list.filter.filter_state'|trans }} </div><div class="filter"><span id="external_filter_status"></span></div>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<table class="table table-striped hover dataTable no-footer" id="table-announcement">
<thead>
<tr>
<th class="col-md-3">
{{ 'message.linkingAnnoucement.list.tab.tab_announcement'|trans }}
</th>
<th class="col-md-1">
{{ 'message.linkingAnnoucement.list.tab.tab_category'|trans }}
</th>
<th class="col-md-1">
{{ 'message.linkingAnnoucement.list.tab.tab_from'|trans }}
</th>
<th class="col-md-1">
{{ 'message.linkingAnnoucement.list.tab.tab_created_date'|trans }}
</th>
<th class="col-md-1">
{{ 'message.linkingAnnoucement.list.tab.tab_diffusion_date'|trans }}
</th>
<th class="col-md-1">
{{ 'message.linkingAnnoucement.list.tab.tab_priority'|trans }}
</th>
<th class="col-md-1">
{{ 'message.linkingAnnoucement.list.tab.tab_time_remaining'|trans }}
</th>
<th class="col-md-1">
{{ 'message.linkingAnnoucement.list.tab.tab_state'|trans }}
</th>
<th class="col-md-1">
{{ 'message.linkingAnnoucement.list.tab.tab_nb_contact'|trans }}
</th>
<th class="col-md-1 no-sort">
</th>
</tr>
</thead>
<tbody class="panel-body">
</tbody>
</table>
</div>
</div>
My symfony controler
public function paginateAction(Request $request)
{
$this->initImgManager();
$formatter = new \IntlDateFormatter(\Locale::getDefault(), \IntlDateFormatter::NONE, \IntlDateFormatter::NONE);
$formatter->setPattern("d MMMM YYYY");
$this->formatter = $formatter;
$length = $request->get('length');
$length = $length && ($length!=-1)?$length:0;
$start = $request->get('start');
$start = $length?($start && ($start!=-1)?$start:0)/$length:0;
$search = $request->get('search');
$filters = [
'query' => #$search['value']
];
$announcements = $this->getDoctrine()->getRepository('AppBundle:Announcement\Announcement')->search(
$filters, $start, $length
);
$output = array(
'data' => array(),
'recordsFiltered' => count($this->getDoctrine()->getRepository('AppBundle:Announcement\Announcement')->search($filters, 0, false)),
'recordsTotal' => count($this->getDoctrine()->getRepository('AppBundle:Announcement\Announcement')->search(array(), 0, false))
);
/**
* #var Announcement $announcement
*/
foreach ($announcements as $announcement)
{
// first image announcement
if ($announcement->getPicturesFiles()->count() > 0)
{
$image = $announcement->getPicturesFiles()->first()->getFilePath();
$handlingAnnouncementFirst = $this->imageHandlingManager->open($image)->zoomCrop(75, 75, 'transparent')->guess(100);
$imageAnnouncementFirst = $this->assetsHelper->getUrl($handlingAnnouncementFirst);
$htmlAnnouncement = '<figure>
<img src="'.$imageAnnouncementFirst.'">
<figcaption>
'.$announcement->getTitle().'
</figcaption>
</figure>';
}
else
{
$htmlAnnouncement = '<figure>
<figcaption>
'.$announcement->getTitle().'
</figcaption>
</figure>';
}
// image user from
if (!empty($announcement->getUsers()->getFile()))
{
$image = $announcement->getUsers()->getFile()->getFilePath();
$handlingUser = $this->imageHandlingManager->open($image)->zoomCrop(45, 45, 'transparent')->guess(100);
$imageUser = $this->assetsHelper->getUrl($handlingUser);
$htmlUserFrom = '<div style="float:left;">
<figure>
<img class="img-circle" src="'.$imageUser.'">
</figure>
</div>
<div style="float:left;">
'.$announcement->getUsers()->getFirstname().' '.$announcement->getUsers()->getLastname().'
</div>';
}
else
{
$htmlUserFrom = '<div style="float:left;">
<div class="job-resume-img-author"></div>
</div>
<div style="float:left;">
'.$announcement->getUsers()->getFirstname().' '.$announcement->getUsers()->getLastname().'
</div>';
}
// remaining time
if (!empty($announcement->getDuration()->getDuration()) && !empty($announcement->getValidationDate()))
{
/** #var AppExtension $twigExtensionService */
$twigExtensionService = $this->get('app.twig_extension');
$requestAttributes = $this->container->get('request')->attributes;
$remainingValidateAnnouncement = $twigExtensionService->remainingtimeFilter($announcement->getDuration()->getDuration(), $announcement->getValidationDate()->format('Y-m-d H:i:s'), $announcement->getId());
}
else
{
$remainingValidateAnnouncement = "-";
}
// status
$htmlStatus = '<span class="label '.$announcement->getStatus()->getClassStatus().'">
'.$announcement->getStatus()->getName().'
</span>';
// actions
$translator = $this->get('translator');
$tradAction = $translator->trans('message.common.btn.lib_action');
$tradView = $translator->trans('message.common.btn.lib_action');
$urlView = $this->generateUrl('avantages_backend_mer_edit', array("id" => $announcement->getId()));
$htmlActions = '<div class="btn-group">
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
'.$tradAction.'
<i class="fa fa-chevron-down" aria-hidden="true"></i>
</button>
<ul class="dropdown-menu pull-right text-left">
<li>'.$tradView.'</li>
</ul>
</div>';
$output['data'][] = [
'announcement' => $htmlAnnouncement,
'category' => $announcement->getCategory()->getName(),
'from' => $htmlUserFrom,
'created_at' => $this->formatter->format($announcement->getCreatedDate()),
'validationDate' => (!empty($announcement->getValidationDate()) ? $this->formatter->format($announcement->getValidationDate()):'-'),
'priority' => $announcement->getPriority()->getName(),
'remainingValidate' => $remainingValidateAnnouncement,
'status' => $htmlStatus,
'nbcontacts' => $announcement->getLinkingsannouncements()->count(),
'htmlActions' => $htmlActions,
];
}
return new Response(json_encode($output), 200, ['Content-Type' => 'application/json']);
Since you are using "bServerSide": true you must implement the filtering logic on your server side just like in the showcase page - Server side source example , the showcase is written in JAVA, but you can get the general idea from the source code that can be found on github, in general you have to read the filtered values from the request (JAVA)
Just like you read the global filter String globalSearch = req.getParameter("search[value]");
You will have to read filtered value for each column (for example first/second coulmns)
String sSearch_0 = req.getParameter("columns[0][search][value]");
String sSearch_1 = req.getParameter("columns[1][search][value]");
Also, in case that you want to populate your select / auto_complete filters with values you have to add to your current JSON the following attributes yadcf_data_0 / yadcf_data_1 / etc'
where each attribute contains a list of strings
For example:
"yadcf_data_0":["KHTML","Webkit","Trident","Misc","Other browsers","Tasman","Presto","Gecko"],
Again, you should read the notes on the showcase page