How to display nested cells in Element UI table? - vue.js

I am using Element UI. I have nested data that I need to display in table. The problem that I can't understand how to display nested data.
Here is my code:
<el-table :data="tableData" stripe border>
<el-table-column width="170" prop="id"></el-table-column>
<el-table-column width="170">
<template slot-scope="scope">
<!-- -->
</template>
</el-table-column>
</el-table>
data section:
tableData: [
{
"id":1,
"nested": [{"name": "mike"}, {"name": "piter"}]
},
{
"id":2,
"nested": [{"name": "maria"}, {"name": "anna"}]
},
]
};
https://jsfiddle.net/3nhb79qc/
I want to display it's like:

One solution is using span-method in Element UI Table
First, flat your data structure by using computed method:
computed: {
expandData() {
return this.tableData.reduce((a, c) => {
const arr = c.nested.map(item => ({
id: c.id,
name: item.name
}))
a = a.concat(arr)
return a
}, [])
}
},
then your data will become:
[
{
"id": 1,
"name": "mike"
},
{
"id": 1,
"name": "piter"
},
{
"id": 2,
"name": "maria"
},
{
"id": 2,
"name": "anna"
}
]
After that define objectSpanMethod and use it in el-table
objectSpanMethod({ row, column, rowIndex, columnIndex }) {
if (columnIndex === 0) {
if (rowIndex % 2 === 0) {
return {
rowspan: 2,
colspan: 1
};
} else {
return {
rowspan: 0,
colspan: 0
};
}
}
}
Demo on jsfiddle

Related

Vuejs get dynamic form values

I have select and input component with made by buefy. Everything is ok till I realize how can I get the data.
I'm sort of new on vuejs. So I will be glad if you help me out.
I'm getting dynamic form from backend
So my question is how can get values these inputs and submit to backend again with getOffer() methot.
Here is my codes;
Input.vue
<template>
<b-field :label="fieldLabel">
<b-input
:name="inputName"
:type="inputType"
:maxlength="inputType == 'textarea' ? 200 : null"
></b-input>
</b-field>
</template>
<script>
export default {
name: "Input",
props: {
inputType: {
type: String,
required: true,
default: "text",
},
inputName: {
type: String,
required: true,
},
fieldLabel: {
type: String,
required: true,
}
}
};
</script>
Home.vue
<template>
<div class="container is-max-desktop wrapper">
<div v-for="element in offer" :key="element.id">
<Input
v-model="element.fieldValue"
:value="element.fieldValue"
:fieldLabel="element.fieldLabel"
:inputType="element.fieldType"
:inputName="element.fieldName"
v-if="element.fieldType != 'select'"
class="mb-3"
/>
<Select
v-model="element.fieldValue"
:fieldLabel="element.fieldLabel"
:options="element.infoRequestFormOptions"
:selectName="element.fieldName"
v-if="element.fieldType == 'select'"
class="mb-3"
/>
</div>
<b-button type="is-danger" #click="getOffer()">GET</b-button>
</div>
</template>
<script>
import axios from "axios";
import Select from "../components/Select.vue";
import Input from "../components/Input.vue";
export default {
name: "Home",
data() {
return {
offer: [],
};
},
components: {
Select,
Input,
},
methods: {
getOfferForm() {
axios({
method: "get",
url: `/GETDYNAMICFORM`,
})
.then((response) => {
this.offer = response.data;
})
.catch(() => {
this.$buefy.toast.open({
duration: 3000,
message: "oops",
position: "is-bottom",
type: "is-danger",
});
});
},
getOffer() {
console.log(this.offer);
},
},
created() {
this.getOfferForm();
},
};
</script>
Example Dynamic Form Response like;
[
{
"id": 58,
"fieldLabel": "Name Surname",
"providerLabel": "Name Surname",
"fieldName": "nmsrnm",
"fieldType": "text",
"fieldValue": null,
},
{
"id": 60,
"fieldLabel": "E-mail",
"providerLabel": "E-mail",
"fieldName": "e_mail_60",
"fieldType": "email",
"fieldValue": null,
},
{
"id": 2,
"fieldLabel": "Budget",
"providerLabel": "Budget",
"fieldName": "bdget",
"fieldType": "select",
"fieldValue": "",
"infoRequestFormOptions": [
{
"id": 1,
"orderNum": 0,
"optionValue": 0,
"optionText": "Select",
"minValue": null,
"maxValue": null
},
{
"id": 2,
"orderNum": 1,
"optionValue": 1,
"optionText": "10-30",
"minValue": 10,
"maxValue": 30
}
]
}
]

How to make multiple search filters work with a for loop in Vue Bootstrap?

I have a table with some filters in each column but when I type anything all the items disappear. The console does not return any error.
Here’s the code:
<template>
<div>
<b-table
id="the-table"
:per-page="perPage"
:current-page="currentPage"
:items="filteredItems"
:fields="fields"
>
<template slot="top-row" slot-scope="{ fields }">
<td v-for="field in fields" :key="field.key">
<input v-model="filters[field.key]" :placeholder="field.label" />
</td>
</template>
</b-table>
<b-pagination v-model="currentPage" :total-rows="rows" :per-page="perPage" aria-controls="the-table"></b-pagination>
</div>
</template>
<script>
import axios from "axios";
export default {
data() {
return {
filters: {
option: "",
style: "",
stock: "",
},
items: [],
fields: [
{ key: "options", label: "Options", sortable: true },
{ key: "style", label: "Style", sortable: true },
{ key: "stock", label: "Stock", sortable: true },
{ key: "type", label: "Type", sortable: true },
{ key: "class", label: "Class.", sortable: true },
],
perPage: 15,
currentPage: 1,
};
},
created() {
this.getTable();
},
methods: {
getTable() {
axios
.get("./data/options.json")
.then((res) => (this.items = res.data))
.catch((error) => console.log(error));
},
},
computed: {
rows() {
return this.items.length;
},
filteredItems() {
return this.items.filter((d) => {
return Object.keys(this.filters).every((f) => {
return this.filters[f].length < 1 || this.filters[f].includes(d[f]);
});
});
},
},
};
</script>
I am trying to filter an array of objects like this:
[{"option": "ABEVH160", "style": "American", "stock": "ABEV3", "type": "CALL", "class": "ITM"},
{"option": "BBAS230", "style": "European", "stock": "BBAS3", "type": "PUT", "class": "ATM"},
{"option": "BBDC180", "style": "American", "stock": "BBDC4", "type": "CALL", "class": "OTM"}]
My goal is to be able to filter each of the columns individually:
Does anyone know what I’m missing?
The problem is with the condition:
return this.filters[f].length < 1 || this.filters[f].includes(d[f]);
d[f] is the full value and this.filters[f] is the search string. Obviously this does not work since it checks if the full word is contained in a substring. Simply invert the condition:
return this.filters[f].length < 1 || d[f].includes(this.filters[f]);
You can see it working here.
What you want seems to be something like DataTable Filter | Filter Row in PrimeVue. I tried to find something similar in the documentation of bootstrap-vue (documentation), but couldn´t find anything like that.
Maybe you are in a stage where you can still implement PrimeVue in your project. I´m using Filter Row from PrimeVue by myself and can recommend it.

How to work with arrays of arrays with vuedraggable

I have to face with problem in my vuejs experiments.
I have two arrays in my data, for example:
columns: [
{ name: "col1" },
{ name: "col2" },
{ name: "col3" },
{ name: "col4" }
],
items: [
{
name: "col1-i1",
id: 1241,
column: "col1"
},
{
name: "col1-i2",
id: 1241242,
column: "col1"
},
{
name: "col2-i1",
id: 1242421,
column: "col2"
}
]
Then I build object which consist of both this arrays so:
computed: {
list() {
return this.columns.map(col => {
return {
column: col,
items: this.items.filter(item => item.column === col.name)
};
});
}
},
After this in my list object I have this structure:
[{
"column": {
"name": "col1"
},
"items": [
{
"name": "col1-i1",
"id": 1241,
"column": "col1"
},
{
"name": "col1-i2",
"id": 1241242,
"column": "col1"
}
]},{
"column": {
"name": "col2"
},
"items": [
{
"name": "col2-i1",
"id": 1242421,
"column": "col2"
}
]},{
"column": {
"name": "col3"
},
"items": []},{
"column": {
"name": "col4"
},
"items": []}]
I try to make draggable items in 4 columns, so:
<div class="column" v-for="(column, index) in list" :key="index">
{{column.column.name}}
<draggable group="a" :list="column.items">
<div class="item" v-for="item in column.items" :key="item.id">{{item.name}}</div>
</draggable>
</div>
</div>
but its not dragged into other column.
How to make it move right.
Example is here https://codesandbox.io/s/elated-aryabhata-uslwb?fontsize=14&hidenavigation=1&theme=dark
The issue is caused by the fact that the list object gets recomputed on change and you end up with the initial object, since the data used to generate it (columns and items) has not changed.
Computed properties by default are getter only, and they only return the computed value. If you want to set the computed value, you will have to define a setter, and change the initial values of columns and items.
But, for this use case, you should generate the list object in mounted hook, and then feed it to draggable component.
data: () => ({
list: [],
columns: [...], // your columns
items: [...] // your items
})
mounted(){
this.list = this.columns.map(col => {
return {
column: col,
items: this.items.filter(item => item.column === col.name)
};
});
}
If you plan to update columns and items dynamically, then you should define a watcher for these properties, and recompute the list.

Ant design vue different data in nested tables

I'm trying to make nested table, in documentation into the nested table they push one same data, how can i change it and push different data for every parent table row?
<script>
const columns = [
{ title: 'Status', dataIndex: 'Status', key: 'Status' },
{ title: 'Date', dataIndex: 'DateCreated', key: 'DateCreated' }
];
const innerColumns = [
{ title: 'Price', dataIndex: 'Price', key: 'Price' },
{ title: 'Status', dataIndex: 'Status', key: 'Status' },
];
const data = [
{
key: 0,
"Status": "u",
"DateCreated": "2019-10-11"
},
{
key: 1,
"Status": "u",
"DateCreated": "2019-09-20"
},
];
const innerData = [
[
{
key: 0,
"Price": 10623,
"Status": "u",
}
],
[
{
key: 0,
"Price": 15084,
"Status": "u",
},
{
key: 1,
"Price": 15084,
"Status": "u",
}
],
];
export default {
data() {
return {
data,
columns,
innerColumns,
innerData,
};
},
};
</script>
<template>
<div class="hello">
<a-table
:columns="columns"
:dataSource="data"
class="components-table-demo-nested">
<a-table
slot="expandedRowRender"
:columns="innerColumns"
:dataSource="innerData[1]"
:pagination="false"
>
</a-table>
</a-table>
</div>
</template>
Here my code
This worked for me
Template:
<a-table :data-source="data" :defaultExpandAllRows="true" v-on:expandedRowsChange="console.log">
<a-table-column title="First Col" data-index="firstCol" />
<template v-slot:expandedRowRender="record, index, indent, expanded">
<a-table :data-source="record.nestedData"
:pagination="false">
<a-table-column title="File Name" data-index="filename" />
</a-table>
</template>
</a-table>
Script:
Vue.component('demo', {
data: function() {
return {
data: [
{
firstCol: '1',
nestedData: [{filename: 'nested 11'}, {filename: 'nested 12'}]
},
{
firstCol: '2',
nestedData: [{filename: 'nested 22'}, {filename: 'nested 22'}]
}
]
}
}
});

Filter on nested (recursive) data ( Vue 2 )

Here is an example of my JSON data :
"data":[
{
"id":01,
"name":"test",
"parent_id":null,
"children":[
{
"id":15,
"name":"subChild",
"parent_id":21,
"children":[
{
"id":148,
"name":"subSubChild",
"parent_id":22,
"children":[
....
]
}
]
}
]
},
I would like to filter this level by level. I have made this method :
computed: {
filteredData: function () {
let filterData = this.filter.toLowerCase()
return _.pickBy(this.data, (value, key) => {
return _.startsWith(value.name.toLowerCase(), filterData)
})
},
This work for only the first "level" and I tried several solutions but none worked for children.
So, I would like to be able to filter by several levels.
If you have an idea!
Thank you
A recursive function could come in handy for this particular purpose.
Try the following approach, and for better view, click on Full page link next to the Run code snippet button down below.
new Vue({
el: '#app',
data() {
return {
filter: '',
maintainStructure: false,
data: [{
"id": 01,
"name": "test",
"parent_id": null,
"children": [{
"id": 15,
"name": "subChild",
"parent_id": 21,
"children": [
{
"id": 148,
"name": "subSubChild",
"parent_id": 22,
"children": []
},
{
"id": 150,
"name": "subSubChild3",
"parent_id": 24,
"children": []
}
]
}]
}]
}
},
methods: {
$_find(items, predicate) {
let matches = [];
for (let item of items) {
if (predicate(item)) {
matches.push(item);
}
else if (item.children.length) {
let subMatches = this.$_find(item.children, predicate);
if (subMatches.length) {
if (this.maintainStructure) {
matches.push({
...item,
children: subMatches
});
}
else {
matches.push(subMatches);
}
}
}
}
return matches;
},
filterBy(item) {
return item.name.toLowerCase().startsWith(this.filter.toLowerCase());
}
},
computed: {
filteredData() {
return this.$_find(this.data, this.filterBy);
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div>
<label>Filter by <code>item.name</code>:</label>
<input v-model.trim="filter" placeholder="e.g. subsub" />
</div>
<div>
<label>
<input type="checkbox" v-model="maintainStructure" /> Maintain structure
</label>
</div>
<hr />
<pre>{{filteredData}}</pre>
</div>
Note that I'm prefixing the function with $_ to sort of mark it as private function (as recommended in this Style Guide) since we're not going to invoke it anywhere else.