Sort by date in React data grid - javascript

Is there a sort by date in the React data grid? If so, how can this be called? In the examples, all sorting works only on the line:
Example 1
Example 2
In example 2, there are columns with dates, but they are sorted as strings.

i have a solution that is really good but not great. really good in that it sorts perfectly but it isn't good if you also want to filter on the column as well.
Basically you are displaying the formated date with a custom formatter and the value you are passing to sort on is the the number of seconds from 1/1/1970 for each date so it will sort right.
And yes, i use functions instead of components for the formatter and headerRenderer values all the time and it works fine.
i work an an intranet so i can't just copy/paste my code so this is just hand typed but hopefully you get the idea.
class GridUtil {
static basicTextCell = (props)=>{
return (<span>{props.value? props.value : ""}</span>)
}
}
class AGridWrapper extends React.Component {
///////////////
constructor(props){
super(props);
this.state={...}
this.columnHeaderData = [
{
key: "dateCompleted",
name: "Date Completed",
formatter: (data)=>GridUtil.basicTextCell({value: data.dependentValues.dateCompletedFormatted}),
getRowMetaData: (data)=>(data),
sortable: true
}
];
}//end constructor
///////////////////////////////////
//Note that DateUtil.getDisplayableDate() is our own class to format to MM/DD/YYYY
formatGridData = !props.inData? "Loading..." : props.inData.map(function(obj,index){
return {
dateCompleted: rowData.compDate? new Date(rowData.compDate).getTime() : "";
dateCompletedFormatted: rowData.compDate? DateUtil.getDisplayableDate(rowData.compDate) : "";
}
});
//////////
rowGetter = rowNumber=>formatGridData[rowNumber];
/////////
render(){
return (
<ReactDataGrid
columns={this.columnHeaderData}
rowGetter={this.rowGetter}
rowsCount={this.formatGridData.length}
...
/>
}

I use react-table I was having the same issue, the solution in case, was to convert the date to a javaScript Date, and also return this as a JSon format, I have to format how the date render in the component. here is a link that shows how I did this.
codesandbox sample

Related

Return different values from computed Vue

I have buttons to set different dates (one for tomorrow and one for in a week) and have made a function for only calculating business days. So the 'tomorrow' button should show either 'Tomorrow' or 'Monday' and the 'week' button should either show 'in 7 days', 'in 8 days' or 'in 9 days' depending on what day of the week it currently is.
These are my two buttons:
<v-btn
:value="dates[0]"
>
{{ buttonText }}
</v-btn>
<v-btn
:value="dates[1]"
>
{{ buttonText }}
</v-btn>
This is my computed for the different dates to postpone and this works just fine:
postponeDays(): DateTime[] {
const today = DateTime.local()
return [
onlyBusinessDays(today, 1),
onlyBusinessDays(today, 7),
]
},
The onlyBusinessDays function looks like this:
export const onlyBusinessDays = (date: DateTime, nrOfDays: number): DateTime => {
const d = date.startOf('day').plus({ days: nrOfDays })
const daysToAdd = d.weekday === 6 ? 2 : d.weekday === 7 ? 1 : 0
return d.plus({ days: daysToAdd })
}
And here is my computed for getting the button text that is not working and I need help with:
buttonText(): DateTime | string {
if (!this.dates[1]) {
if (DateTime.local().plus({ days: 7 }).hasSame(this.dates[1], 'day')) {
return 'in 7 days'
}
if (DateTime.local().plus({ days: 8 }).hasSame(this.dates[1], 'day')) {
return 'in 8 days'
} else return 'in 9 days'
} else {
if (DateTime.local().plus({ days: 1 }).hasSame(this.dates[0], 'day')) {
return 'Tomorrow'
} else return 'Monday'
}
},
I've tried doing the computed for the button text in many different ways but I always end up getting the exact same text for both buttons which feels weird since the postpone function for actually getting the correct dates is working perfectly fine. Anyone that knows what I'm doing wrong? :)
Make buttonText a method instead, so you can put {{ buttonText(dates[0]) }} and {{ buttonText(dates[0]) }}.
As in the Vue docs on computed properties:
Instead of a computed property, we can define the same function as a method. For the end result, the two approaches are indeed exactly the same. However, the difference is that computed properties are cached based on their reactive dependencies. A computed property will only re-evaluate when some of its reactive dependencies have changed.
Your two v-btn elements aren't evaluated with different scopes, so there's no reason for Vue to believe that buttonText will have different values in both cases. What you want conceptually is for buttonText to return different values based on the date you pass in, which fits much closer to a method as far as Vue is concerned.
As on the methods page of the guide:
It is also possible to call a method directly from a template. As we'll see shortly, it's usually better to use a computed property instead. However, using a method can be useful in scenarios where computed properties aren't a viable option.

React render html from a string with a dynamic variable

I am working with react app in typescript. From API, I have this input:
a) list of variable names ["name", "surname"]
b) few strings in form of simple html with variables "<p>Hello, how are you {name}?</p>"
c) number of inputs with variables such as {input1: "name"}
everything as a string/JSON
what i need to do is: render simple html (only few tags) received from API but "create" binding between those dynamic inputs and variables in strings
in static world, result would look like:
[name, setName] = useState("")
<p>Hello, how are you {name}?</p>
<input type="text" onChange={e => setName(e.target.value)}/>
However, all of this is dynamic. String "<p>Hello, how are you {name}?</p>" doesnt get binded to the input on its own.
I tried:
setting variable [vars, setVars] = useState({}), property for each dynamic variable, with
a) dangerouslySetInnerHTML - renders only html (cannot bind the variable inside to the input)
b) react-html-parser - same as above
c) babel.transform - couldnt make it work as this is done dynamically and in browser, it cannot find the right preset, i couldnt make the mimified babel.js work with typescript How to render a string with JSX in React
do you see some easy way? For example how could i use React.createElement to render html with "live" variable inside, represented as {variableName}? Or maybe something out of the box? giving each elemnt a class and finding the class in DOM and editing the text with input change would be probably very non-optimal?
I hope this could be a better example:
{response:
{
variables: ["name", "name2", "mood"],
texts: [
"<em> Hello! My name is {name}</em>",
"<p> Hi {name} ! I am <strong>{name2}</strong> and I feel {mood} today</p>"
],
inputs: [
{
label: "How do i feel?"
input: {mood}
}
]
}
}
EDIT:
This should give you a good idea:
https://stackblitz.com/edit/react-ts-cwm9ay?file=DynamicComponent.tsx
EDIT #2:
I'm pretty good with React and interpolation, it's still in progress (specifically the docs, but the readme is complete) but I'm going to shamelessly plug my ReactAST library
EDIT #3 - If you're interested in doing crazy dynamic interpolation, then you might also want to check out a neat dynamic interpolation (and it's reverse) library
Let's assume this:
{
vars: ['name','surname'],
strings: ["<p>Hello, how are you {name}?</p>","<p> It's night to meet you {name} {surname}"],
inputs: {
input1: 'name',
input2: 'surname'
}
}
Create a component (or set of components) that can do this.
I haven't even ran any of this, but here's the idea. I'll put it in a stackblitz in a bit and polish it up to make sure it works:
const MyDynamicComponent = ({vars,strings,inputs}) => {
const [state,setState] = useState(vars.reduce((acc,var) => ({...acc,[var]:undefined}));
return (
<>
<MyDynamicText vars={vars} strings={strings} state={state}/>
<MyDynamicInputs onChange={(name,val) => setState({[name]:val}) state={state} inputs={inputs}/>
</>
)
}
const MyDynamicText = ({vars,strings,state}) => {
return strings.map((s,i) => s.replaceAll(`{${vars[i]}}`,state[vars[i]])
}
const MyDynamicInputs = ({onChange,inputs,state}) => {
return Object.entries(inputs).map(([inputName,varName]) => <input key={inputName} onChange={e => onChange(varName,e.target.value)} value={state[varName]}/>
}

React DatePicker loads with default value but then will not change when state changes

I have a component which renders x amount of DateInputs based on x number of charges. The date inputs should be rendered with the default value loaded from the server. Once the onChange is triggered it updates the state in the parent and prints out the console with the selected date using the computedProperty. To give more context here is an image of the page:
A you can see, a datepicker is rendered for each charge and the user needs the ability to modify the charge date. So in terms of data flow, the onChange in the child component triggers the method handleChargesDateChange in the parent, as the state needs to be stored in parent.
At the minute, the default charge date is rendered in the dropdown, but when onChange is executed then date input value does not change. However, the method prints out the correct date that was selected. Again, for more context, here is a screenshot of the data stored in parent when child executes onChange on the date picker.
As you can see, the date values get modified when the user selects one from each datepicker. However, the actual datepicker value stays the same. I can get it to work where it renders with an empty date for each datepicker and when the user selects a new date, it will render the datepicker with the selected date. It also stored the value in parent. But I am really struggling to render the defalt value, but when it is changed update the datepicker with the user selected value... I hope this makes sense.. Anyway here's the code. So the method which gets triggered in parent:
handleChargesDateChange = (e) => {
const date = e.target.value;
const momentDate = moment(date);
const chargeId = e.target.name;
console.log(date);
console.log(momentDate);
this.setState({
updatedChargeDates: [
...this.state.updatedChargeDates,
{[chargeId]: date}
]
}, () => console.log(this.state.updatedChargeDates))
}
This then gets passed down (around 3 children deep) via:
<Grid.Row>
<Grid.Column width={16}>
<OrderSummary
order={this.state.order}
fuelTypes={this.state.fuelTypes}
vehicleClasses={this.state.vehicleClasses}
deviceTypes={this.state.deviceTypes}
chargeTypes={this.state.chargeTypes}
featureTypes={this.state.featureTypes}
itemGroups={this.state.itemGroups}
itemTypes={this.state.itemTypes}
updateChargeDates={this.handleChargesDateChange}
chargeDates={this.state.updatedChargeDates}
/>
</Grid.Column>
I will skip the various other children components that the method gets passed to and go straight to the effected component. So the method then lands at Charges:
export class Charges extends Component {
constructor(props) {
super(props);
this.state = {
currentItem: 0,
newDate: '',
fields: {}
}
}
render() {
const {charges, chargeTypes, updateChargeDates, chargeDates} = this.props;
console.log('Props method', charges);
if (charges.length === 0) { return null; }
return <Form>
<h3>Charges</h3>
{charges.map(charge => {
console.log(charge);
const chargeType = chargeTypes.find(type => type.code === charge.type) || {};
return <Grid>
<Grid.Row columns={2}>
<Grid.Column>
<Form.Input
key={charge.id}
label={chargeType.description || charge.type}
value={Number(charge.amount).toFixed(2)}
readOnly
width={6} />
</Grid.Column>
<Grid.Column>
<DateInput
name={charge.id}
selected={this.state.newDate}
***onChange={updateChargeDates}***
***value={chargeDates[charge.id] == null ? charge.effectiveDate : chargeDates[charge.id]}***
/>
</Grid.Column>
</Grid.Row>
</Grid>
})}
<Divider hidden />
<br />
</Form>
}
I have highlighted the culprit code with *** to make it easier to follow. I am using a ternary type operator to determine which value to display, but it doesnt seem to update when i select a value.. I thought that from the code I have, if the initial chargeDates[charge.id] is null then we display the charge.effectiveDate which is what it is doing, but soon as I change the date i would expect chargeDates[charge.id] to have a value, which in turn should display that value...
I really hope that makes sense, im sure im missing the point with this here and it will be a simple fix.. But seems as im a newbie to react, im quite confused..
I have tride to follow the following resources but it doesnt really give me what I want:
Multiple DatePickers in State
Daepicker state
If you need anymore questions or clarification please do let me know!
Looking at your screenshot, updatedChargeDates (chargeDates prop) is an array of objects. The array has indexes 0, 1 and 2, but you access it in the DateInput like this chargeDates[charge.id] your trying to get the data at the index 431780 for example, which returns undefined, and so the condition chargeDates[charge.id] == null ? charge.effectiveDate : chargeDates[charge.id], will always return charge.effectiveDate.
You can fix this in handleChargesDateChange by making updatedChargeDates an object, like this :
handleChargesDateChange = (e) => {
const date = e.target.value;
const momentDate = moment(date);
const chargeId = e.target.name;
console.log(date);
console.log(momentDate);
this.setState({
updatedChargeDates: {
...this.state.updatedChargeDates,
[chargeId]: date
}
}, () => console.log(this.state.updatedChargeDates))
}

How made a deep copy that can resolve a problem with a table

Im using the table material-table, where the set a new element to my object, called tableData. So that feature create an issues to my code and API because I update using Patch. I implemented a conventional and also custom deep copy of my object to avoid the table add this element to my object.
But for some reason it isnt working. This is an example of the table where you can see how it added the tableData to the example object. https://codesandbox.io/s/lx2v9pn91m Please check the console
Above I showed the real object, and in the element 5 array, appear the tableData after each render. Extra comment, the property of the table I passed to table is: data={MyRealObject.element5}
This is the struct of my real object:
MyRealObject{
element1: boolean,
element2: boolean ,
element3: Array ,
element4: Array ,
Cards: Array ,
}
Card{
Id: number ,
CardNumber : number ,
CardFormat : {CardFormatObject},
//here where appear the tableData after each render
}
CardFormatObject{
Id: number ,
CardNumberLength : number ,
CardFormatLength : number ,
Default:boolean ,
Name: string ,
}
This is the lastest custom deep copy I did and didnt work:
deepcopy(MyRealObject:MyRealObject):MyRealObject{
let salvaCard=[]
for(let i=0;i<user.Cards.length;i++){
salvaCard[i]=this.deepCardCopy(MyRealObject.Cards[i])
}
return{
element1: MyRealObject.element1,
element2: MyRealObject.element2,
element3: [...MyRealObject.element3], //I know here is not deep but isnt important now, and it isnt affected
element4: [...MyRealObject.element4],
Cards: salvaCard,
}as MyRealObject
}
public deepCardCopy(card:Card):Card{
return {
Id:card.Id,
CardNumber:card.CardNumber,
CardFormat:{...card.CardFormat}
} as Card;
}
//////////////////////////////
This are others deep code that I used and dont works, I share to save you time:
--------old solution 1(i dont like it, you can lose element if there are null)------------------------------------------------
// Cards: JSON.parse(JSON.stringify(MyRealObject.Cards)),
---------old solution 2-------------------------------------------
// MyRealObject.Cards.map(card=>{
// const {tableData,...record}=card
// return record
// }),
setState is an async function. That means you won't see the result right after calling it. You have to wait for setState to be finished. There is a callback-Parameter in setState which you can use to get the result of this.state
handleSelectedID = rowData => {
const selectedID = rowData.map(item => item.id);
this.setState({ selectedID }, () => {
// setState is async
// this is the callback
console.log(this.state.selectedID);
});
};
I changed your example-code a bit to give you an example of it: https://codesandbox.io/s/1olz33pmlj
I found a solutions, it is passsing the clone in the moment i passed the object to the table, for example:
data={MyRealObject.map(x => Object.assign({}, x))}
The issues was, that the people that create the table didnt make a clone to the data, the use the same reference, so it will be better do this to avoid problems that some one in the component didnt clone the object.
Kindly, noted, DONT used this way
data={...MyRealObject} or data={Object.assign({}, MyRealObject)}
The CORRECT is:
data={MyRealObject.map(x => Object.assign({}, x))}
Both expression look similiar but it isnot the same.

Trouble with Bootstraps typeahead in angular 5

I am having a difficult time getting Bootstraps typeahead in angular 5 working would appreciate some advice. The problem I have is that I don't know how to set the input field to equal the city + state for exaple "New york, NY" in bootstraps search method example. I am new to Typescript and the new fat arrow feature in JavaScript any help would be greatly appreciated.
model array of objects
public model: any;
example of data that I am getting
{
"city":"New York",
"latitude":40.7127837,
"longitude":-74.0059413,
"state":"New York",
"stateCode":"NY"
}
Search method here I am trying to set the location items to filter 'city,'+'state'
search = (text$: Observable<string>) => text$
.debounceTime(200)
.distinctUntilChanged()
.map(term => term.length < 2 ? [] : this.locationItems.filter(item => item.city.toLowerCase().indexOf(term.toLowerCase()) > -1).slice(0, 10));
The arrow function inside the last map in your chain needs to map (transform) the value of the string the user has typed into the search box to an array of items that will be presente to the user as suggestions.
You start nice, by askin if term is only one character long (or an empty string) and do nt even run the search, imediatelly returning empty array. For the other part, you need to find which items you want to present to the user.
This part depends on your business logic, but I assume that you want user to be searching by either state or stateCode? Anyway, this part is your business logic and you can change and improve it according to your busniess model. I'm giving a very simply function in the code below.
// util function
// returns true if the "term" can be used to match the "item" from your data
// change this according to your business logic
function matches(term: string, item: Item): boolean {
return item.state.toLowerCase().includes(term.toLowerCase())
|| item.stateCode.toLowerCase().includes(term.toLowerCase())
}
The lambda in the last map can be like this.
term => {
if (term.length < 2) {
return []
}
// arary of matching results
const matching = this.filter(item => matches(term, item))
// now we transform this to the format you specified
const formatted = matching.map(item => `${item.state}, ${item.stateCode}`)
return formatted
// you can also .slice(0, 10) here like you did in your example to keep the number of suggestions no more than 10
}

Categories

Resources