How to handle complex rowspan in angular 6? - javascript

I have created table from the below mentioned JSON which works fine. I have certain condition that needs to be handled. the function which i used is also mentioned here.I also attached output image for the same.Help for the same is highly appreciated... Thanks in advance
Conditions :
if email row is empty need to remove that particular row.
Let's say value2 has one value in email, in that case it should be displayed.
rows = [];
generateTable() {
if (!this.data) {
return;
}
this.rows.push([
{
text: this.data.e_o_name,
rowspan: 0
}
]);
let maxRowSpan = 0;
this.data.matching_details.forEach((detail, i) => {
const elemRowSpan = Math.max(detail.matching_attributes.length, 1);
maxRowSpan += elemRowSpan;
if (i > 0) {
this.rows.push([])
}
this.rows[this.rows.length - 1].push({
text: detail.me_value,
rowspan: elemRowSpan
});
detail.matching_attributes.forEach((attr, j) => {
if (j > 0) {
this.rows.push([])
}
const mail = attr.me_list[0];
this.rows[this.rows.length - 1].push(
{
text: attr.me_name,
rowspan: 1
},
{
text: mail.me_email_list.map(({ me_value }) => me_value).join(', '),
rowspan: 1
},
{
text: mail.me_percent,
rowspan: 1
}
);
})
});
this.rows[0][0].rowspan = maxRowSpan;
}
```
#Josn : #
```
{
"e_id":"1234",
"e_o_name":"Contact_info",
"matching_details":[
{
"me_value":"value1",
"matching_attributes":[
{
"me_id":"1234",
"me_name":"28 sai",
"me_list":[
{
"me_type":"Email ID",
"me_email_list":[
{
"me_value":"a#gmail"
},
{
"me_value":"b#gmail"
}
],
"me_percent":"100"
}
]
},
{
"me_id":"5678",
"me_name":"29 meena",
"me_list":[
{
"me_type":"Email ID",
"me_email_list":[
{
"me_value":"c#gmail.com"
},
{
"me_value":",d#gmail.com"
}
],
"me_percent":"100"
}
]
}
]
},
{
"me_value":"value2",
"matching_attributes":[
{
"me_id":"1234",
"me_name":"rimzim",
"me_list":[
{
"me_type":"Email ID",
"me_email_list":[
{
"me_value":"p#gmail"
},
{
"me_value":"q#gmail"
}
],
"me_percent":"100"
}
]
},
{
"me_id":"5678",
"me_name":"ranu",
"me_list":[
{
"me_type":"Email ID",
"me_email_list":[
{
"me_value":"t#gmail.com"
},
{
"me_value":",u#gmail.com"
}
],
"me_percent":"100"
}
]
}
]
}
]
}

Seems like you want to put in column (attr) level validations, so in the html while looping through it you will need to implement the checks
https://stackblitz.com/edit/angular-zm1ap1?file=src/app/app.component.html
<table>
<tbody>
<tr>
<th>contact</th>
<th>ty</th>
<th>ed</th>
<th>mail</th>
<th>percent</th>
</tr>
<tr *ngFor="let row of rows">
<!-- check if row is empty or could add additional check such
row[3].text (email) is true
-->
<ng-container *ngIf='row && row.length > 0'>
<td [attr.rowspan]='row[0].rowspan'>{{row[0].text}}</td>
<td *ngIf='row.length > 1' [attr.rowspan]='row[1].rowspan'>{{row[1].text}}</td>
<td *ngIf='row.length > 2' [attr.rowspan]='row[2].rowspan'>{{row[2].text}}</td>
<td *ngIf='row.length > 3' [attr.rowspan]='row[3].rowspan'>{{row[3].text}}</td>
<td *ngIf='row.length > 4' [attr.rowspan]='row[4].rowspan'>{{row[4].text}}</td>
</ng-container>
</tr>
</tbody>
</table>

Related

Vue.draggable - swapping between two list

I have a problem. I need to organaze two draggable table like swapping.
1 table: have 7 fixed items.
2 table: Other items.
When you swapping items from the second table to the first, a dialog box should appear to confirm swapping.
I did swap for first list, but not understand how to do for two table.
my code now:
<template>
<table>
<draggable v-model="items" tag="tbody" :move="handleMove" #end="handleDragEnd" :options="{animation:500}">
<tr v-for="item in items" :key="item.id">
<td>{{ item.id }}</td><td>{{ item.name }}</td><td>{{ item.age }}</td>
</tr>
</draggable>
</table>
</template>
<script>
import draggable from "vuedraggable";
export default {
components: {
draggable,
},
data() {
const items = [
{ id: 1, name: "Bianka Effertz", age: 37 },
{ id: 2, name: "Mckayla Bogan", age: 20 },
{ id: 3, name: "Estevan Mann", age: 17 },
{ id: 4, name: "Cloyd Ziemann", age: 55 }
]
return { items }
},
computed: {
orders() {
return this.items.map(x => x.id)
}
},
methods: {
handleDragEnd() {
this.$toast.show('dragEnd')
this.futureItem = this.items[this.futureIndex]
this.movingItem = this.items[this.movingIndex]
const _items = Object.assign([], this.items)
_items[this.futureIndex] = this.movingItem
_items[this.movingIndex] = this.futureItem
this.items = _items
},
handleMove(e) {
const { index, futureIndex } = e.draggedContext
this.movingIndex = index
this.futureIndex = futureIndex
return false // disable sort
}
}
</script>

how can i achieve pagination in vue js

please i'm trying to paginate data but i'm getting this error in my console
Property or method "$index" is not defined on the instance but referenced during render.
this is my html template which displays the data in which i'll want to paginate
<div id="heroes">
<table class="table table-striped">
<thead>
<tr>
<td>Hero Name</td>
<td>Universe</td>
</tr>
</thead>
<tbody>
<tr
v-for="hero in heroes "
v-show="setPaginate($index)"
>
<td>{{ hero.name }}</td>
<td>{{ hero.universe }}</td>
</tr>
</tbody>
</table>
<div id="pagination">
<a
href="#"
class="btn btn-default"
v-for="page_index in paginate_total"
#click.prevent="updateCurrent(page_index + 1)"
>
{{ page_index + 1 }}
</a>
</div>
</div>
this is my script tag
<script>
export default {
data() {
return {
current: 1,
heroes: [
{ name: "Wolverine", universe: "Marvel" },
{ name: "Batman", universe: "DC" },
{ name: "Beast", universe: "Marvel" },
{ name: "Superman", universe: "DC" },
{ name: "Wonder Woman", universe: "DC" },
{ name: "Iceman", universe: "Marvel" },
{ name: "Black Panther", universe: "Marvel" },
{ name: "Beast Boy", universe: "DC" },
{ name: "Captain America", universe: "Marvel" },
{ name: "Hawkgirl", universe: "DC" },
{ name: "Cyclops", universe: "Marvel" },
{ name: "Green Lantern", universe: "DC" },
],
paginate: 5,
paginate_total: 0
};
},
created() {
this.paginate_total = this.heroes.length / this.paginate;
},
methods: {
setPaginate: function(i) {
console.log(i);
if (this.current == 1) {
return i < this.paginate;
} else {
return (
i >= this.paginate * (this.current - 1) &&
i < this.current * this.paginate
);
}
},
setStatus: function(status) {
this.status_filter = status;
this.$nextTick(function() {
this.updatePaginate();
});
},
updateCurrent: function(i) {
this.current = i;
},
updatePaginate: function() {
this.current = 1;
this.paginate_total = Math.ceil(
document.querySelectorAll("tbody tr").length / this.paginate
);
}
}
};
</script>
i got the example from codepen which works fine when using it on codepen but i'm getting an error in my code ,here's the link
https://codepen.io/lorena-tapia/details/VwZzpRZ
please how can i go about this
The $index is not defined anywhere, you could define it in the v-for loop like :
<tr v-for="(hero,index) in heroes " v-show="setPaginate(index)">
<td>{{ hero.name }}</td>
<td>{{ hero.universe }}</td>
</tr>

splice is creating NULL entry while removing the entry from an array

In my Angular5 application when I tried to remove the entries from the peoples array which is contained in selectedPersonArray using splice keyword is occasionally creating null entry in the peoples array. The code I have done looks like this:
import { Component } from '#angular/core';
import { DragulaService } from 'ng2-dragula/ng2-dragula';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
selectedPersonArray = [];
peoples = [
{
id: 7,
firstName: 'Sobin 7',
lastName: 'Thomas 7'
}
];
people = [
{
id: 1,
firstName: 'First Name 1',
lastName: 'Last Name 1'
},
{
id: 2,
firstName: 'First Name 2',
lastName: 'Last Name 2'
},
{
id: 3,
firstName: 'First Name 3',
lastName: 'Last Name 3'
},
{
id: 4,
firstName: 'First Name 4',
lastName: 'Last Name 4'
}
];
toggleItemInArr(arr, item) {
const index = arr.indexOf(item);
index === - 1 ? arr.push(item) : arr.splice(index, 1);
}
addThisPersonToArray(person: any, event) {
if (!event.ctrlKey) {
this.selectedPersonArray = [];
}
this.toggleItemInArr(this.selectedPersonArray, person);
}
isPersonSelected(person: any) {
return (this.selectedPersonArray.indexOf(person) !== -1);
}
constructor(private dragula: DragulaService) {
dragula.setOptions('second-bag', {
copy: function (el, source) {
return source.id === 'source';
},
removeOnSpill: true,
copySortSource: false,
accepts: function(el, target, source, sibling) {
return target.id !== 'source';
}
});
}
ngOnInit() {
this.dragula
.out
.subscribe(value => {
for(let select of this.selectedPersonArray){
let i= 0;
for (let entry of this.peoples) {
if(entry.id == select.id){
let index = this.people.indexOf(select.id);
if (this.peoples.length >0){
this.peoples.splice(i, 1);
}
}
i++;
}
}
console.log("on after loop "+JSON.stringify( this.peoples));
this.selectedPersonArray.length = 0;
});
}
}
app.component.html
<table border=1 bgcolor="yellow">
<tbody id ='source' [dragula]='"second-bag"' [dragulaModel]="people">
<tr *ngFor="let person of people" >
<td>{{person.id}}</td>
<td>{{person.firstName}}</td>
<td>{{person.lastName}}</td>
</tr>
</tbody>
</table>
<table border=2>
<tbody id ='dest' [dragula]='"second-bag"' [dragulaModel]="peoples">
<tr *ngFor="let person of peoples" (click)="addThisPersonToArray(person, $event)" [class.active]="isPersonSelected(person)">
<td>{{person.id}}</td>
<td>{{person.firstName}}</td>
<td>{{person.lastName}}</td>
</tr>
</tbody>
</table>
Actually what I want was is to delete multiple items selected using control key from 'dest' container of ng2-dragular second-bag.And it is working but it is creating null entries also
Can you try this?
this.peoples
.filter(people => !this.selectedPersonArray
.find(selectdPeople => people.id === selectdPeople.id)
);
I like the answer using filter and find. Here's another alternative using a loop.
I worked on a jsfiddle with what I suspect you were trying to do. Is this this is similar to what you intended?
I updated the search to work backwards so that the splice wouldn't alter the parts of the array that had not been searched through yet.
The jsfiddle can be found here: https://jsfiddle.net/vboughner/wfeunv2j/
this.selectedPersonArray = [
{
"id": "hello1",
"name": "something1"
},
];
this.peoples = [
{
"id": "hello1",
"name": "something1"
},
{
"id": "hello2",
"name": "something2"
},
{
"id": "hello3",
"name": "something3"
},
];
console.log(this.selectedPersonArray);
console.log(this.peoples);
for (let select of this.selectedPersonArray) {
for (let i = this.peoples.length - 1; i >= 0; i--) {
if (select.id == this.peoples[i].id) {
this.peoples.splice(i, 1);
}
}
}
console.log("on after loop " + JSON.stringify( this.peoples));
this.selectedPersonArray.length = 0;
The output looks like this:
on after loop [{"id":"hello2","name":"something2"},{"id":"hello3","name":"something3"}]

How to count the number of rows containing a certain value?

I'm using AngularJS and I have a table that I populate using ng-repeat. Check this short example:
http://jsfiddle.net/sso3ktz4/
How do I check how many rows I have with a certain value? For example, in the fiddle above, how do I check how many rows I have with the word "second"? It should return "3".
AngularJS answers are preferred, although I also have JQuery available.
Thanks!
updated controller code is below, where $scope.findRowCount is required function
var myApp = angular.module('myApp', []).controller('MyCtrl', MyCtrl);
function MyCtrl($scope) {
$scope.items = [{
name: 'first',
examples: [{
name: 'first 1'
}, {
name: 'first 2'
}]
}, {
name: 'second',
examples: [{
name: 'second'
}, {
name: 'second'
}]
}];
$scope.findRowCount=function(value){
var count=0;
angular.forEach($scope.items, function(item, i){
if(item.name==value){
count=count+1;
}
angular.forEach(item.examples, function(exp, j){
if(exp.name==value){
count=count+1;
}
})
});
console.log("count"+count);
return count;
}
var result=$scope.findRowCount("second");
console.log(result);
}
http://jsfiddle.net/p3g9vyud/
Try this way
var myApp = angular.module('myApp', []).controller('MyCtrl', MyCtrl);
function MyCtrl($scope) {
$scope.items = [{
name: 'first',
examples: [{
name: 'first 1'
}, {
name: 'first 2'
}]
}, {
name: 'second',
examples: [{
name: 'second'
}, {
name: 'second'
}]
}];
//Get sum based on the label
$scope.getTotalByLabel = function(keyword)
{
$scope.totalSecond = 0;
angular.forEach($scope.items, function(value, key) {
if(value.name == keyword)
{
$scope.totalSecond += 1;
}
angular.forEach(value.examples, function(val, k) {
if(val.name == keyword)
{
$scope.totalSecond += 1;
}
});
});
return $scope.totalSecond;
}
}
th,
td {
padding: 7px;
text-align: left;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.1/angular.min.js"></script>
<div ng-app="myApp">
<div ng-controller="MyCtrl">
<table border="1">
<tbody ng:repeat="i in items">
<tr>
<td>{{i.name}}</td>
<td>{{$index}}</td>
</tr>
<tr ng:repeat="e in i.examples">
<td>{{e.name}}</td>
<td>{{$index}}</td>
</tr>
</tbody>
</table>
<b>Total of second</b>: {{getTotalByLabel('second')}}
</div>
</div>

How to sort dates in table using vue.js

My table is based on the Grid Component Example in Vue.js' website
I'm having problem with sorting dates inside the table. I get all the table data from server side as JSON. So in the codes provided, I just mocked the data in var mockDataFromServerSide.
Here is the code: https://jsfiddle.net/5w1wzhvw/3/
HTML file:
<!-- component template -->
<script type="text/x-template" id="grid-template">
<table>
<thead>
<tr>
<th v-for="key in columns"
v-on:click="sortBy(key)"
:class="{active: sortKey == key}">
{{key | capitalize}}
<span class="arrow"
:class="sortOrders[key] > 0 ? 'asc' : 'dsc'">
</span>
</th>
</tr>
</thead>
<tbody>
<tr v-for="
entry in data
| filterBy filterKey
| orderBy sortKey sortOrders[sortKey]">
<td v-for="key in columns">
{{entry[key]}}
</td>
</tr>
</tbody>
</table>
</script>
<!-- demo root element -->
<div id="demo">
<form id="search">
Search <input name="query" v-model="searchQuery">
</form>
<demo-grid
:filter-key="searchQuery">
</demo-grid>
</div>
Js file:
var gridColumns = ['name', 'date'];
var mockDataFromServerSide = [
{ name: 'Chuck Norris', date: "01 Dec 2016" },
{ name: 'Bruce Lee', date: "23 Apr 2005" },
{ name: 'Jackie C', date: "30 Jan 2012" },
{ name: 'Jet Li', date: "20 Apr 2006" }
];
// register the grid component
Vue.component('demo-grid', {
template: '#grid-template',
props: {
filterKey: String
},
data: function () {
var sortOrders = {}
gridColumns.forEach(function (key) {
sortOrders[key] = 1
})
return {
sortKey: '',
sortOrders: sortOrders,
columns: gridColumns,
data: mockDataFromServerSide
}
},
methods: {
sortBy: function (key) {
this.sortKey = key
this.sortOrders[key] = this.sortOrders[key] * -1
}
}
})
// bootstrap the demo
var demo = new Vue({
el: '#demo',
data: {
searchQuery: ''
}
})
I also tried to add a filter to the date. The sort is correct but the displayed dates are shown as "Thu Apr 02 2016 00:00:00 GMT+0800 (China Standard Time)". I want the dates to be displayed as 02 Apr 2016.
Added filter Code: https://jsfiddle.net/kr1m5de5/1/
HTML file (added filter):
<!-- component template -->
<script type="text/x-template" id="grid-template">
<table>
<thead>
<tr>
<th v-for="key in columns"
v-on:click="sortBy(key)"
:class="{active: sortKey == key}">
{{key | capitalize}}
<span class="arrow"
:class="sortOrders[key] > 0 ? 'asc' : 'dsc'">
</span>
</th>
</tr>
</thead>
<tbody>
<tr v-for="
entry in data
| filterBy filterKey
| orderBy sortKey sortOrders[sortKey]
| datesFilter">
<td v-for="key in columns">
{{entry[key]}}
</td>
</tr>
</tbody>
</table>
</script>
<!-- demo root element -->
<div id="demo">
<form id="search">
Search <input name="query" v-model="searchQuery">
</form>
<demo-grid
:filter-key="searchQuery">
</demo-grid>
</div>
JS file (added filter):
var gridColumns = ['name', 'date'];
var mockDataFromServerSide = [
{ name: 'Chuck Norris', date: "01 Dec 2016" },
{ name: 'Bruce Lee', date: "23 Apr 2005" },
{ name: 'Jackie C', date: "30 Jan 2012" },
{ name: 'Jet Li', date: "20 Apr 2006" }
];
// register the grid component
Vue.component('demo-grid', {
template: '#grid-template',
props: {
filterKey: String
},
filters: {
datesFilter: function (data) {
data.forEach(function (row) {
row.date = new Date(row.date);
});
return data;
}
},
data: function () {
var sortOrders = {}
gridColumns.forEach(function (key) {
sortOrders[key] = 1
})
return {
sortKey: '',
sortOrders: sortOrders,
columns: gridColumns,
data: mockDataFromServerSide
}
},
methods: {
sortBy: function (key) {
this.sortKey = key
this.sortOrders[key] = this.sortOrders[key] * -1
}
}
})
// bootstrap the demo
var demo = new Vue({
el: '#demo',
data: {
searchQuery: ''
}
})
Please let me know how to fix it or if there is a better way to do it.
I solved this by making a TableHeader component it says semantic cause i used semantic-ui... sorry for the spanglish in the code, must of 'em are cognates anyway. Also, this code is working, but if you see improvements to the code/answer let me know please!
As you can see, i really don't sort at front... i make a new request with the sorted items.
<template>
<th #click="cycleSort(sth, $event)">
<span><span>{{ sth.texto }} </span><i class="icon" :class="sth.icon"></i><sub v-if="sth.posicion > 0"><small>{{ sth.posicion }}</small></sub></span>
</th>
</template>
<script>
export default {
name: "SemanticTableHeader",
props: {
sth : {
type : Object,
default: () => {}
},
sths : {
type : Array,
default: () => { return [] }
},
filtrosOrder : {
type : Array,
default: () => { return [] }
},
isSearching : {
type : Boolean,
required : true
}
},
methods: {
cycleSort(sth, event) {
if(this.isSearching == true){
return false;
}
switch (sth.direction) {
case null:
sth.direction = 'asc';
sth.icon = 'sort ascending';
break;
case 'asc':
sth.direction = 'desc';
sth.icon = 'sort descending';
break;
case 'desc':
sth.direction = null;
sth.icon = 'sort disabled';
break;
default:
sth.direction = null;
sth.icon = 'sort disabled';
}
this.manejaCambioHeader(sth);
},
manejaCambioHeader: _.debounce(function (sth) {
var self = this;
console.log(this.filtrosOrder);
let auxUser = _.find(this.filtrosOrder, function(o) { return o.id == sth.id; });
if( auxUser != null ){
auxUser.direction = sth.direction;
if(auxUser.direction == null){
for (var i=0 ; i < this.filtrosOrder.length ; i++){
if (this.filtrosOrder[i].id === auxUser.id) {
let auxSths = _.find(self.sths, function(o) { return o.id == sth.id; });
auxSths.posicion = 0;
this.filtrosOrder.splice(i, 1);
}
}
}
}else{
this.filtrosOrder.push({ id: sth.id, direction: sth.direction });
}
for (var i=0 ; i < self.filtrosOrder.length; i++){
let auxSths = _.find(this.sths, function(o) { return o.id == self.filtrosOrder[i].id; });
auxSths.posicion = i + 1;
}
console.log(this.filtrosOrder);
this.$emit('sortHeaderChanged', sth);
}, 400),
},
}
</script>
<style lang="css" scoped>
th span{
cursor: pointer !important;
-webkit-touch-callout: none; /* iOS Safari */
-webkit-user-select: none; /* Safari */
-khtml-user-select: none; /* Konqueror HTML */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* Internet Explorer/Edge */
user-select: none; /* Non-prefixed version, currently supported by Chrome and Opera */
}
i.icon{
margin: 0em -0.2em 0em 0em;
}
</style>
In my Index views i just load the component and use it like this
<template>
<table>
<thead>
<tr>
<semantic-table-header v-for="sth in sths" :key="sth.key"
:sth="sth"
:sths="sths"
:isSearching="isSearching"
:filtrosOrder="filtros.orderBy"
#sortHeaderChanged="fetchIndex"
></semantic-table-header>
<th></th>
</tr>
</thead>
<tbody>
<tr v-for="contact in contacts" :key="contact.key" :class="[contact.justAdded ? 'justAdded' : '']">
</tr>
</tbody>
</table>
</template>
export default {
name: "ContactsIndex",
data:() => ({
filtros:{
orderBy:[
{ id: 'nombre', direction: 'asc' } // orderBy is calculated through the headers component
]
},
sths:[
{ id: 'nombre', texto: 'Nombre', icon: 'sort ascending', direction: 'asc', posicion: 1 },
{ id: 'telefonos', texto: 'Teléfono(s)', icon: 'sort disabled', direction: null, posicion: 0 },
{ id: 'emails', texto: 'Correo Electrónico(s)', icon: 'sort disabled', direction: null, posicion: 0 },
{ id: 'estatus', texto: 'Estatus', icon: 'sort disabled', direction: null, posicion: 0 }
],
contacts: [],
}),
created() {
this.fetchIndex();
},
methods: {
resetFilters() {
// this function is to reset filters and headers
Object.assign(this.$data.filtros, this.$options.data().filtros);
this.$data.sths = this.$options.data().sths;
this.fetchIndex();
},
fetchIndex() {
let self = this;
// this is a wrapper i made for an axios post call you can replace it with a normal call
singleIndexRequest('/api/v1/contacts/index', self).then(response => {
self.contacts = response.data.contacts;
});
},
}
}

Categories