sorting not working properly angular 4 - arrays

I have various tables and I'm trying to sort table using https://www.npmjs.com/package/ngx-order-pipe. I followed their documentation and everything works fine except it is not sorting correctly the columns (here in the example, 'Rank' column)
For example I have a response like this:
"collection": [
{
"name": "John",
"age" : "25",
"details": [
{
"final_rank": "150"
}
]
}
{
"name": "Mark",
"age" : "19",
"details": [
{
"final_rank": "254"
}
]
}
Here's my HTML:
<table>
<thead>
<tr>
<th (click)="setOrder('name')">Name</th>
<th (click)="setOrder('age')">Age</th>
<th (click)="setOrder('final_rank')">Rank</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let Data of collection | orderBy: order:reverse:'case-insensitive'">
<td class="text-truncate">{{Data.name}}</td>
<td class="text-truncate">{{Data.age}}</td>
<td class="text-truncate" *ngIf="!isArray(Data.details)">
<tr> {{Data.details.final_rank}} </tr>
</td>
<td class="text-truncate" *ngIf="isArray(Data.details)"><tr *ngFor="let rankData of Data.details"> {{rankData.final_rank}} </tr></td>
</tr>
</tbody>
</table>
component.ts
order;
reverse = false;
isArray(obj: any) {
return Array.isArray(obj)
}
getData() {
this.http.get('**')
.subscribe(data => {
console.log(data);
});
}
setOrder(value) {
if (this.order === value) {
this.reverse = !this.reverse;
}
this.order = value;
console.log(this.order);
}

Your problem seems to be that you try to sort by a field that is inside of an array of the actual object. I suspect that your library doesn't know how to do the sorting (and that probably rightly so). So what you should do is to transform your data in a format, where it can be sorted.
At some point in your application you have the data:
const originalData = [{
'name': 'John',
'age': '25',
'details': [
{
'final_rank': '150'
}
]
},
{
'name': 'Mark',
'age': '19',
'details': [
{
'final_rank': '254'
}
]
}];
Whata you want to do now is to take that data and to convert it to something else. In this example I want to get the max final_rank value of any of the details items. That max value will be used for sorting. You might want to use another way of defining what value to use, but for this example the max value should do fine.
We can use a map function to transform each value of your original data:
const mappedData = originalData.map(item => ({
// This will do a shallow copy of all the fields of the original object
...item,
// With reduce we can easily find the max ```final_rank``` value
maxRank: item.details.reduce(
// +current.final_rank converts the string to a number
(max, current) => +current.final_rank > max ? current.final_rank : max,
0
)
}));
This should yield a new array which should look the same as your original data, except for an additional maxRank field on the root object.
The resulting object would look something like this:
const mappedData = [{
'name': 'John',
'age': '25',
'maxRank': 150,
'details': [
{
'final_rank': '150'
}
]
},
{
'name': 'Mark',
'age': '19',
'maxRank': 254,
'details': [
{
'final_rank': '254'
}
]
}];
Now you should be able to do sorting based on the maxRank field.

Related

React table, how to show blank column if object value does not match

How can I render blank data in specific column if value in object does not match
Data from API
[
{
"parent": "parent1",
"ouid": 1,
"child1": "tom",
"child2": "bob"
},
{
"parent": "parent2",
"ouid": 2,
"child1": "smith",
"child2": "steven"
},
{
"parent": "parent3",
"ouid": 1,
"child1": "mack",
"child2": "jack"
}
]
Column mapping
const defaultColumns = [
table.createDataColumn("parent", {
id: "parent",
}),
table.createGroup({
header: "ouid",
columns: [
table.createDataColumn("child1", {
id: "child1",
}),
table.createDataColumn("child2", {
id: "child2",
}),
],
}),
table.createGroup({
header: "ouid1",
columns: [
table.createDataColumn("child1", {
id: "child1",
}),
table.createDataColumn("child2", {
id: "child2",
}),
],
}),
];
Table Mapping
<table border={1}>
<thead>
{instance.getHeaderGroups().map((headerGroup) => (
<tr key={headerGroup.id}>
{headerGroup.headers.map((header) => (
<th key={header.id} colSpan={header.colSpan}>
{header.isPlaceholder ? null : header.renderHeader()}
</th>
))}
</tr>
))}
</thead>
<tbody>
{instance.getRowModel().rows.map((row) => (
<tr key={row.id}>
{row.getVisibleCells().map((cell) => (
<td key={cell.id}>{cell.renderCell()}</td>
))}
</tr>
))}
</tbody>
</table>
Output
Expected Output
I want different column group to be rendered based on value of 'ouid' and it's child column will always be 'child1','child2'
rest all common column will be filled 'parent' in this case, new column group will be created for another 'ouid' value and so on..., do let me know if I need to alter data from api in a specific way to achieve this.
#tanstack react-table version - 8.0.0-beta.4 (I can't update to latest stable, but can downgrade to v7 if this is achievable by it)
row.getVisibleCells return
What's the return of row.getVisibleCells() look like?

Get data id in array object vuejs

how to display value data based on index array. here I make a modal edit data, I have a json like the following
[
{
"ID": 3,
"idusers": 3,
"skills": "Go",
"profiency": "Expert",
},
{
"ID": 48,
"skills": "laravel",
"profiency": "laravel",
},
{
"ID": 47,
"skills": "Vue",
"profiency": "Expert",
}
]
table data that I display
<tr v-for="(skill, index) in getSkills" :key="skill.ID">
<td>{{ index + 1 }}</td>
<td>{{ skill.skills }}</td>
<td>{{ skill.profiency }}</td>
<td class="text-xs-center">
<td><div v-if="editBtn == true">
<v-btn class="mr-3" x-small fab v-on:click="handleEdit(item,index)" color="primary"><v-icon>mdi-cancel</v-icon></v-btn>
</td>
</tr>
and this my modal edit
<v-dialog v-model="skillForm" width="500">
<v-container>
<v-text-field outlined dense>
{{profiency}}
</v-text-field>
</v-container>
</v-dialog>
my method
export default {
name: "Skills",
data: function() {
return {
formAddSkills: "",
skillForm: false,
editBtn: "",
skills: {
skills: "",
profiency: "",
},
};
},
computed: {
...mapState({ getSkills: (state) => state.resume.Skills }),
dataSkills() {
return this.skills;
},
},
methods: {
handleEdit(item, index) {
this.skillForm = true;
this.editBtn = true;
this.profiency = item.profiency
// console.log(item)
console.log(index)
},
}
}
the question is how to display data based on ID, when I click the edit button it appears and enters the text field form
Pass skill from your method as a parameter
#click="handleEdit(skill,index)
Then declare a variable, currentObject, and then equate it this.currentObject = skill inside the method.
Then you can access currentObject.id via the v-model binded to your text field.

Using ng-repeat on nested array of objects

Below is my JSON object and I am trying to show the same in html table.
$scope.results = {
"data": [
{
"name": "Sam",
"details": [
{
"official": [
{
"address1": "Link road",
"pincode": 76755554
},
{
"address1": "K main road",
"pincode": 9766565
}
]
},
{
"name": "John",
"details": [
{
"official": [
{
"address1": "Old college road",
"pincode": 11111
},
{
"address1": "near east side",
"pincode": 6555446
}
]
}
]
}
]
}
]
}
I have used ng-repeat to achieve the below output. But I am not getting the expected result
This is the code which I have tried and got stuck JSFIDDLE
Any idea on how to achieve my expected result will be really helpful.
You may have to tweak this slightly if your data structure can get more complicated when there are more levels, but this should reformat your data and flatten everything into an array of people without all the different levels.
$scope.peopleFlat = [];
function flattenPeople(people) {
people.forEach((person) => {
$scope.peopleFlat.push({
name: person.name,
addresses: person.details[0].official
});
if (person.details.length > 1) {
flattenPeople([person.details[1]]);
}
});
}
flattenPeople($scope.people);
Then you can use a combination of ng-repeat-start and ng-repeat-end to make it work using rowspan instead of a nested <table> element.
<table class="table table-bordered table-condensed">
<tr>
<th>Name</th>
<th>Address</th>
<th>Pincode</th>
</tr>
<tr ng-repeat-start="person in peopleFlat">
<td rowspan="{{person.addresses.length || 1}}">{{person.name}}</td>
<td>{{person.addresses.length ? person.addresses[0].address1 : ''}}</td>
<td>{{person.addresses.length ? person.addresses[0].pincode : ''}}</td>
</tr>
<tr ng-repeat-end ng-repeat="address in person.addresses.slice(1)">
<td>{{address.address1}}</td>
<td>{{address.pincode}}</td>
</tr>
</table>

How to paginate a table built with 2 arrays?

var mainItems = [
{
name: Car,
subItems: [
{ name: SubCar1 },
{ name: SubCar2 }
]
},
{
name: Food,
subItems: [
{ name: SubFood1 },
{ name: SubFood2 }
]
}
]
<table>
<tbody data-pagination-id="tableItem" data-current-page="cItemCtrl.currentPage"
data-dir-paginate="mainItem in cItemCtrl.mainItems | itemsPerPage: cItemCtrl.numberPerPage"
data-total-items="cItemCtrl.total" data-status="on">
<tr data-ng-repeat="subItem in mainItem.subItems">
<td>{{mainItem.name}}</td>
<td>{{subItem.name}}</td>
</tr>
</tbody>
</table>
<dir-pagination-controls data-pagination-id="tableItem" on-page-change="cItemCtrl.pager(x)"></dir-pagination-controls>
I'd like to know how paginate this table above, because the array of subItems is ignoring the filter "itemsPerPage" in "data-dir-paginate". I need a way
to paginate controlling both arrays. Is that possible?

order by two columns in angular js (average, time seconds)

I'm trying to sort two columns with angular js, column average and another time in seconds, but the seconds not sorted correctly.
I currently returns this:
I'm doing something as a ranking.
my html:
<tr ng-repeat="user in ranking | orderBy:['-promedio','tiempo']" >
<td >{{ user.usuario }}</td>
<td > {{ user.promedio }} </td>
<td > {{ user.tiempo}} </td>
</tr>
my ctrl:
EncuestApp.controller('ResultadoCtrl', function ($scope) {
$scope.ranking =[
{
"id":"5",
"tiempo":"15.737",
"promedio":"0.4",
"usuario":"rfmartinez"
},
{
"id":"6",
"tiempo":"35.694",
"promedio":"0.8",
"usuario":"mm"
},
{
"id":"7",
"tiempo":"303.328",
"promedio":"0.8",
"usuario":"mn"
},
{
"id":"8",
"tiempo":"40.327",
"promedio":"0.8",
"usuario":"mnn"
}
];
});
which is the best way to solve it?
thanks
You have stored all numbers as strings (e.g. "10.5" instead of just 10.5). Therefore AngularJS sorts your entries by the textual representation of the numbers. Change all numbers to actual numbers in your data model:
EncuestApp.controller('ResultadoCtrl', function ($scope) {
$scope.ranking =[
{
"id": 5,
"tiempo": 15.737,
"promedio": 0.4,
"usuario": "rfmartinez"
},
{
"id": 6,
"tiempo": 35.694,
"promedio": 0.8,
"usuario": "mm"
},
{
"id": 7,
"tiempo": 303.328,
"promedio": 0.8,
"usuario": "mn"
},
{
"id": 8,
"tiempo": 40.327,
"promedio": 0.8,
"usuario": "mnn"
}
];
});

Resources