I have a sorting issue in my project. After sorting I got a result like this in the image
<td className="dashboard_table-cell" title={'Created Date: ' + Queue.CreatedDate}>{Queue.CreatedDate}</td>
what format will I apply to my code to display the correct sorting order?
I found it in my own way,
sort(event){
if(event.target.id === 'CreatedDate'){
gridData = _.orderBy(gridData, (o) => moment(o[event.target.id])._d,
order[event.target.id] ? 'asc' : 'desc');
}
else
gridData = _.orderBy(gridData, (o) => typeof o[event.target.id] === 'string' ? o[event.target.id].trim().toLowerCase() : o[event.target.id], order[event.target.id] ? 'asc' : 'desc');
}
<th id="CreatedDate" className="dashboard_table-head" onClick={this.sort}>Created Date {order.CreatedDate ? <i id="CreatedDate" className="fa fa-sort-asc" /> : <i id="CreatedDate" className="fa fa-sort-desc" />}</th>
..............................................................
<td className="dashboard_table-cell" title={'Created Date: ' + Queue.CreatedDate}>{moment(Queue.CreatedDate).format('MM-DD-YYYY HH:mm:ss')}</td>
you should try sort by time like
createdDate.getTime()
so for every date, you will have you will compare
createdDate.getTime() of each item.
for every Date() the value we get from .getTime() will be unique and be incrementing.
Related
Explaining better, a client can have in the database that I receive the API, say, camera width, camera size and camera weight, and another client can have camera color, camera thickness, camera validity, camera depth, camera... So each client has different filters and different amount of filters. I need to make a generic code that works for any case, here is my code:
{APIData.filter(data => data.Result !== optionsData &&
(data.InspectionTime.replace(/-/g, "").slice(0, 8) >= startDate &&
data.InspectionTime.replace(/-/g, "").slice(0, 8) <= endDate))
.map((data, id) => {
const result = data.Result;
return (
<tr
key={id}
className="table-row"
onClick={() => {
setActiveRow(id);
setImageTest(id);
}}>
<td
className="table-data-date">
{data.InspectionTime}
</td>
<td
className="table-data-state">
{result == "-2" ? "Processado" : result == "-1" ? "Falha" :
result == "0" ? "Reprovado" : result == "1" ? "Aprovado" :
result == "2" ? "Ignorado" : null}
</td>
</tr>
)
})}
I already have in my code the result and date filters that are fixed for each client, I just need to implement these other filters that vary for each one, how can I do this within my code?
This is a JSX gist I'm displaying on the page.
rows.push(
<tr key={i}>
<td>{this.state['Person Name'] && this.state['Person Name'][i]}</td>
<td>{this.state['Amount'] && this.state['Amount'][i]}</td>
{this.state['Memo'] && this.state['Memo'].length > 0 ? <td>{this.state['Memo'][i]}</td> : undefined}
{this.state['Comment'] && this.state['Comment'].length > 0 ? <td>{this.state['Comment'][i]}</td> : undefined}
{this.state['Incurred Date'] && this.state['Incurred Date'].length > 0 ? <td>{this.state['Incurred Date'][i]}</td> : undefined}
{this.state['Entry Date'] && this.state['Entry Date'].length > 0 ? <td>{this.state['Entry Date'][i]}</td> : undefined}
<td>{this.state['Billable'] && this.state['Billable'][i]}</td>
<td>{this.state.fileName === 'expenses.csv' ? 'Expense' : 'Time'}</td>
</tr>
)
Somehow the conditions that are falsy still display empty <td>s to the table. What did I miss?
Empty columns are shown above.
You don't need to use ternary operators at all. Simply just chain &&'s.
{this.state.Memo && this.state.Memo[i] && <td>{this.state.Memo[i]}</td>}
{this.state.Comment && this.state.Comment[i] && <td>{this.state.Comment[i]}</td>}
{this.state['Incurred Date'] && this.state['Incurred Date'][i] && <td>{this.state['Incurred Date'][i]}</td>}
{this.state['Entry Date'] && this.state['Entry Date'][i] && <td>{this.state['Entry Date'][i]}</td>}
In addition, your array seems to be badly formatted:
// 2 separate variables for the same data?
this.memo = ['a', 'b'];
this.comment = ['comment', 'comment2'];
// Why not make it an array like this?
this.rows = [
{
Memo: 'a',
Comment: 'comment'
},
{
Memo: 'b',
Comment: 'comment2'
}
];
Then you can simply do:
this.rows.map(row => (
<tr key={row.id}>
<td>{row['Person Name']}</td>
<td>{row['Amount']}</td>
{this.state.hasMemos && <td>{row.Memo}</td>}
...
</tr>
)}
A <td> shouldn't be conditional on a row level, it should be conditional on a table level. You can't simply skip TD's if there isn't data for that row, as it will throw off the whole row by shifting columns over. You should either display N/A, an empty <td></td> for rows that may not have data, or hide them entirely on a table level via something like this.state.hasMemos, if there are any memos.
If you're using the new array structure I have listed, you can use this function to determine if any row has a memo:
this.array.some(row => row.Memo);
This will return true if any row has a Memo, thus either hiding the <td> for the entire table, or displaying it for every row.
I'm having a problem creating a search filter, I use the * ngFor in the module and the name usually works like this:
(game.name.toLowerCase().includes(typed))
But the platforms that are coming in array only work when I put the index:
(game.platform[i].toLowerCase().includes(typed))
However this platform is dynamic and I can not use a for or something of the genre inside the filter
Json
[
{
"name":"GTA",
"platform":[
"xbox",
"playstation"
]
}
]
Component
<tr *ngFor="let game of (games | searchGame:SearchedText.value)">
<td>{{game.name}}</td>
<td>{{game.category}}</td>
<td>{{game.platform | separator}}</td>
// "platform":["Playstation 4","Xbox One"]
<td>{{game.price | currency}}</td>
<td>{{game.quantity}}</td>
<td>{{game.production ? 'Sim' : 'Não'}}</td>
<td>{{game.description}}</td>
<td><i class="fa fa-pencil icon" aria-hidden="true" (click)="staticModal.show()"></i></td>
<td><i class="fa fa-times icon" aria-hidden="true" (click)="staticModal.show()"></i></td>
</tr>
Pipe
transform(game, typed){
typed = typed.toLowerCase();
return game.filter( game =>
(game.name.toLowerCase().includes(typed)) ||
(game.category.toLowerCase().includes(typed)) ||
(game.platform.toLowerCase().includes(typed)) || // Error in this line
(game.price.toLowerCase().includes(typed)) ||
(game.quantity.toLowerCase().includes(typed)) ||
(game.production.toLowerCase().includes(typed)) ||
(game.description.toLowerCase().includes(typed))
);
}
If I understand your question correctly, your filter is not doing what you want.
The first pipe parameter should be an array. I think just 'any' works but 'any[]' is more clean :-)
You are doing:
tranform(game: any,...)
Try this:
transform(game: any[], typed: string){
....
return game.filter(game => game.name.toLowerCase().indexOf(typed) > -1 ||
....
Further in the beginning your search token (typed) might be null or empty.
For that you should add the following to return the array unfiltered. Otherwise you will not see anything in your *ngFor.
if (typed == null || typed == "")
return game;
Hope this helps a little.
Edit: Replace the erroring line with
game.filter(game => game.platforms.some(el => el.toLocaleLowerCase().includes(typed)))
I'm trying to conditionally apply a class to my component using an expression like this:
.map(function(list, index) {
<div className={"myClass " + (position === index ? 'active' : null)}>
}
But it keeps adding null as class, with an end result like this:
<div class="myClass active">...
<div class="myClass null">...
This is a simple example, with only 2 class names, so I could just replace null with the default class name. But in a more complex layout, I would need to duplicate the same name over and over again.
Is there a better approach to solve this problem?
You could use an empty string '' instead of null like:
.map(function(list, index) {
<div className={"myClass " + (position === index ? 'active' : '')}>
}
Also map should return a value:
.map(function(list, index) {
return <div className={"myClass " + (position === index ? 'active' : '')}>;
}
If you have multiple classes, you might consider building the list of classes from an array:
var classes = ["myClass"];
if (position === index) {
classes.push('active');
}
return (
<div className={classes.join(' ')}>
...
</div>
);
You can also consider using a helper function that will generate the className string from an object like this:
var classes = {
myClass: true,
active: position === index
};
classnames is one such utility (not the only one).
Remove the space from "myClass " to "myClass", then replace null with an empty string ""
.map(function(list, index) {
<div className={"myClass" + (position === index ? 'active' : "")}>
}
just use https://www.npmjs.com/package/classnames:
usage example:
<div className={cn({"active": position === index })} ></div>
Use && short-circuiting: className={"myClass " + (position === index && 'active')}
In this way, if position === index is false, because we are using &&, we short-circuit. JS skips over 'active' and we just move on with our lives.
React Solution:
className={`myClass ${index ? "active" : ""}`}
Different syntax
className={`myClass ${index && "active"}`}
I'm still stuck with a OrderBy issue. Data comes from $http, and looks like this:
[
{
"number":1,
"timetable": [
{
"name":"Station1",
"time":"2016-05-18T18:14:00.000Z"
},
{
"name":"Station2",
"time":"2016-05-18T18:18:00.000Z"
}
]
},
{
"number":2,
"timetable": [
{
"name":"Station1",
"time":"2016-05-18T18:24:00.000Z"
},
{
"name":"Station2",
"time":"2016-05-18T18:28:00.000Z"
}
]
}
]
So what I need is to view rows where name is for example Station2, and I need them in order by time. The rows aren't ordered by time, nor by number. Amount of timetable rows varies, so row numbers don't help either. Is it possible to order them inside ng-repeat, in style of "OrderBy time where name='Station2' "?
EDIT:
At the moment I'm showing the results without any ordering, only with filtering. Current PHP:
<tr ng-repeat="x in rows | limitTo:5">
<td>
<a href="aikataulu.php?n={{x.number}}">
<span class="label label-primary line-{{x.lineID}}">{{x.lineID}}</span> {{x.number}}
</a>
</td>
<td>
<span ng-repeat="y in x.timetable | limitTo:-1">{{y.name}}</span> //This is for showing the destination
</td>
<td>
<span ng-repeat="y in x.timetable | filter:{'name':'<?php echo $as;?>'}: true | limitTo:1">{{y.time | date:'HH:mm'}}</span>
</td>
</tr>
$as is the station to be shown. So, now the order of the list comes straight from the JSON order, so it varies a lot.
You can use the comparator as an additional argument for the filter.
So you would expand your code to:
<span ng-repeat="y in x.timetable | filter:{'name':'<?php echo $as;?>'}: true : myComparator | limitTo:1">{{y.time | date:'HH:mm'}}</span>
You need a regexp to see if the compared string values are dates and you can do something similar to:
$scope.myComparator= function (a, b) {
// regex to see if the compared values are dates
var isDate = /(-(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)-)/g;
// sorting dates
if (isDate.test(a.value)) {
var aDate = new Date(a.value), bDate = new Date(b.value);
return aDate.getTime() < bDate.getTime() ? -1 : 1
}
// default sorting
return a.index < b.index ? -1 : 1
}
A thing worth noting is you would need a different regular expression to find your date format. Something along the lines of the following might be sufficient:
/(\d{4})-(\d{2})-/g
NOTE: This is untested code and serves only as a guide to the right approach. Make sure your version of AngularJS supports the comparator as a filter argument.
You can order an array of objects by one of their properties with a filter function like this one:
angular.module('yourApp').filter('orderObjectBy', function() {
return function(items, field, reverse) {
var filtered = [];
angular.forEach(items, function(item) {
filtered.push(item);
});
filtered.sort(function (a, b) {
return (a[field] > b[field] ? 1 : -1);
});
if(reverse) filtered.reverse();
return filtered;
};
}
);
Use it like this
<div ng-repeat="elem in data | orderObjectBy:'number'">
// ...
</div>