Difference of days between two dates in React.JS using moment - reactjs

I made a function which calculates difference between two dates, but i dont know how to take(?) data to it using inputs. I also tried to return diffInDays; and instead of that use this.setState({ diffInDays: diffInDays }). I've tried many combinations but none of them gave me the right result.
calculate(dateFrom, dateTo) {
if (!dateFrom || !dateTo) {
return null;
}
const arrival = moment(dateFrom);
const departure = moment(dateTo);
if (arrival.isAfter(departure)) {
return null;
}
const diffInDays = departure
.endOf('day')
.diff(arrival.startOf('day'), 'days');
return diffInDays;
}
those are the inputs I created:
this one is for the beginning
<div className="form-group">
<label htmlFor="dateFrom">Date From: </label>
<input
type="date"
className="form-control"
id="dateFrom"
onChange={this.onChangeDateFrom}
value={this.state.dateFrom}
name="dateFrom"
/>
</div>
and this one is for the end
<div className="form-group">
<label htmlFor="dateTo">Date to: </label>
<input
type="date"
className="form-control"
id="dateTo"
onChange={this.onChangeDateTo}
value={this.state.dateTo}
name="dateTo"
/>
</div>
and this is the one where I want to see the difference
<div className="form-group">
<label htmlFor="daysDiff">Days Difference: </label>
<input
type="number"
className="form-control"
id="daysDiff"
required
value={this.state.daysDiff}
name="daysDiff"
readOnly
/>
</div>
Picking the dates works fine. They also save in the MongoDB, the problem appears only with the difference of days.

You should use
departure.diff(arrival)
and for getting only day difference
departure.diff(arrival,'days')

Related

Warning in Chrome for updating "value" attribute in React without onChange

I am creating a form. There is a radio input that chooses the value of the next input. The code works are written, but I get a warning in the Chrome console. I've tried adding onChange() several different ways but all I get are errors. How do I fix this warning.
const [lyeConcentration, setLyeConcentration] = useState("33.33%");
const [lyeUnits, setLyeUnits] = useState("percentage");
<div onChange={e => setLyeConcentration(e.target.value)}>
<input type="radio" id="lyePercentage" name="lyeUnits" value="33.33%" defaultChecked />
Percentage
<input type="radio" id="lyeRatio" name="lyeUnits" value="2:1" />
Ratio
</div>
<div>
<label htmlFor="lyeConcentration">Lye Concentration</label>
<input id="lyeConcentration" value={lyeConcentration} type="text" className="lyeConcentration" autoComplete="off" />
</div>
try the code below, i added onChange to both radio input and the text input.
I think its actually complaning about the text input.
const [lyeConcentration, setLyeConcentration] = useState("33.33%");
const [lyeUnits, setLyeUnits] = useState("percentage");
return (
<>
<div>
<input type="radio" id="lyePercentage" name="lyeUnits" value="33.33%" onChange={e => setLyeConcentration(e.target.value)} defaultChecked />
Percentage
<input type="radio" id="lyeRatio" name="lyeUnits" onChange={e => setLyeConcentration(e.target.value)} value="2:1" />
Ratio
</div>
<div>
<label htmlFor="lyeConcentration">Lye Concentration</label>
<input id="lyeConcentration" value={lyeConcentration} type="text" className="lyeConcentration" autoComplete="off" onChange={e => setLyeConcentration(e.target.value)}/>
</div>
</>
)

React 'fetch' response successful, but setState not working

I'm new to React, working through a ".Net core CRUD with React" tutorial and trying to tweak it along the way to suit my own needs.
The page I'm dealing with here is an Add/Edit entry page. It works fine for rendering a default form with default values but doesn't render anything if the values are collected from a fetch call.
The important details are below:
interface AddPortfolioProjectDataState {
title: string;
projectData: PortfolioProject;
loading: boolean;
}
The page is told to render as follows:
public render() {
let contents = this.state.loading
? <p><em>Loading Project...</em></p>
: this.renderCreateForm(this.state.projectData.type, this.state.projectData.tech);
return (
<div>
<h1>{this.state.title}</h1>
<h3>Project</h3>
<hr />
{contents}
</div>
)
}
If I want to add a new item, therefore using a default PortfolioProject object with default values, it works fine. However, if I want to edit an old entry, I have to grab it from the server, like so:
fetch('api/StuartAitkenWebsite/GetPortfolioProject/' + projID)
.then(response => response.json() as Promise<PortfolioProject>)
.then(data => {
this.setState({ title: "Edit", loading: false, projectData: data });
});
In the debug console on Firefox, I can see the whole server process runs smoothly:
GET http://localhost:62669/api/StuartAitkenWebsite/GetPortfolioProject/2
Response payload: {"id":2,"name":"Particles Sim","projectDate":"2017-01-01T00:00:00","projectDurationWeeks":1,"type":"Desktop App","tech":"C++, SFML","views":0,"creationDate":"2018-10-22T00:00:00","modifiedDate":"2018-10-22T00:00:00","status":1}`
It gives a JSON output of the payload too, which I can't easily copy-paste here so I'll give a screenshot:
There are no server error responses, no React errors, nothing.
But that's as far as it gets.
The page remains showing 'loading', even though the data is there and ready and wants to be displayed.
From this, I can gather that the final step of the fetch call is not succeeding, because this.setState({ title: "Edit", loading: false, projectData: data }); is clearly not having any effect on the page data.
I have other fetch calls which look exactly the same but work fine. I can't see what I'm missing here.
The one and the only difference I notice is this:
When I use this component to create a fresh 'Add Project' form, the state is set like so:
this.state = {
title: "Create",
loading: false,
projectData: new PortfolioProject,
};
But when I do it from the API, it's set like so:
this.setState({
title: "Edit",
loading: false,
projectData: data
});
The successful version uses this.state, and the unsuccessful version uses this.setState
I don't know what this can mean though. As I said, no errors are being thrown, I'm sticking to the tutorial format, and it works fine in other parts of the project.
Thanks.
UPDATE
I've put a log in at the point where renderCreateForm() is called. It seems setState is actually working. Therefore, the problem must be in renderCreateForm() so I'll post that code below. Sorry it's sort of large.
private renderCreateForm(projectTypes: string, projectTech: string) {
console.log(this.state.loading); // "false"
console.log(this.state.projectData); //"Object { id:1, name: "Muon Detector".. etc
//so the render is getting the data
return (
<form onSubmit={this.handleSave}>
<div className="form-group row" >
<input type="hidden" name="Id" value={this.state.projectData.id} />
</div>
<div className="form-group row" >
<label className=" control-label col-md-12" htmlFor="Name">Name</label>
<div className="col-md-4">
<input className="form-control" type="text" name="Name" defaultValue={this.state.projectData.name} required />
</div>
</div>
<div className="form-group row" >
<label className=" control-label col-md-12" htmlFor="ProjectDate">Project Date</label>
<div className="col-md-4">
<input className="form-control" type="date" name="ProjectDate" defaultValue={this.state.projectData.creationDate.toDateString()} required />
</div>
</div >
<div className="form-group row" >
<label className=" control-label col-md-12" htmlFor="ProjectDurationWeeks">Project Duration (weeks)</label>
<div className="col-md-4">
<input className="form-control" type="text" name="ProjectDurationWeeks" defaultValue={this.state.projectData.projectDurationWeeks.toString()} required />
</div>
</div >
<div className="form-group row" >
<label className=" control-label col-md-12" htmlFor="Type">Project Type</label>
<div className="col-md-4">
<input className="form-control" type="text" name="Type" defaultValue={this.state.projectData.type} required />
</div>
</div >
<div className="form-group row" >
<label className=" control-label col-md-12" htmlFor="Tech">Project Tech</label>
<div className="col-md-4">
<input className="form-control" type="text" name="Tech" defaultValue={this.state.projectData.tech} required />
</div>
</div >
<div className="form-group row" >
<input type="hidden" name="Views" value={this.state.projectData.views} />
</div>
<div className="form-group row" >
<input type="hidden" name="CreationDate" value={this.state.projectData.creationDate.toDateString()} />
</div>
<div className="form-group row" >
<input type="hidden" name="ModifiedDate" value={this.state.projectData.modifiedDate.toDateString()} />
</div>
<div className="form-group row" >
<input type="hidden" name="Status" value={this.state.projectData.status} />
</div>
<div className="form-group">
<button type="submit" className="btn btn-default">Save</button>
<button className="btn" onClick={this.handleCancel}>Cancel</button>
</div >
</form>
)
}
UPDATE 2: Added some screenshots showing how things appear so far.
How the main data table page looks:
If I click 'Add New', it works:
(the 'Save' option works there too. Data posts to the server and will list on the main portfolio page)
Clicking Edit for any of the entries does not work, it gets this far:
The 'Loading Project...' text comes from the render() call for this page, as is shown in the code posted at the top of this post.
The page is supposed to look exactly like the 'Create' page (2nd screenshot), but with the title being 'Edit', and with input values populated from the given data.
The solution was absurd, but may certainly help others...
The renderCreateForm() method (as shown in Update 1 of the post) was not working because of the .toDateString() method I was using in a few of the inputs.
I changed it to .toString() and now everything works.
For example, with an input like this:
<input className="form-control" type="date" name="ProjectDate" defaultValue={projectData.creationDate.toDateString()} required />
I changed it to this:
<input className="form-control" type="date" name="ProjectDate" defaultValue={projectData.creationDate.toString()} required />
Note the defaultValue property of the input.
Repeat for all cases of .ToDateString(), and it now works,
Amazing that this didn't bring up an error anywhere. I thought Typescript and all these various frameworks were supposed to get around the issue of Javascript silently failing like that. This has been my longest and most time-wasting 'silent fail error' ever, by a very long margin.

React OnInputChange handler with various input types with TypeScript?

I have multiple input fields:
<label>
<span className="codes__input-label">Count</span>
<input
type="number"
value={this.state.count}
onChange={this.onInputChange}
/>
</label>
<label>
<span className="codes__input-label">One time Usage</span>
<input
type="text"
value={this.state.distributor}
onChange={this.onInputChange}
/>
</label>
<label>
<span className="codes__input-label">One time Usage</span>
<input
type="checkbox"
value={this.state.oneTimeUsage}
onChange={this.onInputChange}
/>
</label>
It seems repetitive to have one handler for each event, so I want to have one handler for all of them.
What is a good approach?
Here is my best attempt. I get a long TypeScript errors on the setState lines.
onInputChange(event: React.FormEvent<HTMLInputElement>): void {
switch (event.currentTarget.type) {
case "number":
this.setState({[event.currentTarget.name]: event.currentTarget.valueAsNumber});
break;
case "text":
this.setState({[event.currentTarget.name]: event.currentTarget.value});
break;
case "checkbox":
this.setState({[event.currentTarget.name]: event.currentTarget.checked});
break;
}
}
FYI, I don't know Typescript but your problem is not related to this.
First thing I see, you don't have name attributes for your input's. So, add them. The second problem is you don't have a checked value for the checkbox, add it, too.
If you haven't already bound onInputChange method, bind it. Then it should work as expected.
<label>
<span className="codes__input-label">Count</span>
<input
name="count"
type="number"
value={this.state.count}
onChange={this.onInputChange}
/>
</label>
<label>
<span className="codes__input-label">One time Usage</span>
<input
name="distributor"
type="text"
value={this.state.distributor}
onChange={this.onInputChange}
/>
</label>
<label>
<span className="codes__input-label">One time Usage</span>
<input
name="oneTimeUsage"
type="checkbox"
checked={this.state.oneTimeUsage}
onChange={this.onInputChange}
/>
</label>
Though, if you don't need valueAsNumber directly and can use the value itself then do some number manipulation, there is a nicer method generally people use for this method.
onInputChange = (e) => {
const { target, target: { name } } = e;
const value = target.type === 'checkbox' ? target.checked : target.value;
this.setState({
[name]: value
});
}

Calculate percentage based on input fields in Angularjs

I am using AngularJS. I have two input fields: total marks and marks obtained, as well as a 3rd field which is percentage. Now after entering total marks and marks obtained, I need to calculate the percentage automatically.
Here is my html :
<div class="form-group">
<label class="control-label">Total Marks</label>
<input type="text"
class="form-control input_field"
name="totalMarks"
data-ng-model="student.totalMarks" />
</div>
<div class="form-group">
<label class="control-label">Marks Obtained</label>
<input type="text"
class="form-control input_field"
name="marksObtained"
data-ng-model="student.marksObtained" />
</div>
<div class="form-group">
<label class="control-label">Percentage</label>
<input type="text"
class="form-control input_field"
value={{ ((student.marksObtained/student.totalMarks)*100).toFixed(2) }}
name="rank"
data-ng-model="student.rank" />
</div>
Can anyone tell me how to auto generate rank field with percentage as soon as I enter marks obtained. Currently, I am doing as shown above but it is not working.
Not sure that your calcul for percentage is exact, but this Plunkr is working with a watcher on student properties.
app.controller('MainCtrl', function($scope){
$scope.$watchGroup(['student.totalMarks', 'student.marksObtained'], function(){
if($scope.student.marksObtained !== undefined && $scope.student.totalMarks !== undefined)
$scope.student.rank = (($scope.student.marksObtained/$scope.student.totalMarks)*100).toFixed(2);
})
});
https://plnkr.co/edit/NsNge0mzrgjjUgBZ332p?p=preview
You can try assigning your calculation inline, like this:
<input ng-model="student.rank" ng-value="student.rank = ((student.marksObtained/student.totalMarks)*100).toFixed(2)">
Running Example on JsFiddle
It does not work because your input is linked to a ng-model.
There is two ways to solve this problem :
Setup a watcher on student.totalMars and student.marksObtained, and update student.rank with new value.
Do not link your percentage to student.rank and just set the value as you did.
You can check here for the second way.
In your controller, add the following function and variable :
function getPercentage() {
// dividing by zero is bad !!!
let ratio = $scope.student.marksObtained / ($scope.student.totalMarks || 1);
return (ratio * 100).toFixed(2);
}
$scope.student.percentage = getPercentage();
Then change the last <input /> of your view to :
<input type="text"
class="form-control input_field"
ng-value="student.percentage"
name="rank" />
If you want your input to have two-way data binding, use ng-model instead of ng-value.

how to pass the value of one input to another input's onBlur event in reactjs?

I have two date time fields on reactjs form as
<div>
<input type="text" name="startdate" value={this.state.startDate} onBlur={this.validateStartDate} />
<input type="text" name="enddate"value={this.state.endDate} onBlur={this.validateEndDate}/>
</div>
I want to validate the startdate, whether it is less than endDate or not,
How to pass the endDate value to the validateStartDate function?
or
If there is a better way to validate the input date fields, please provide the same.
I see you're storing your endDate as a state. So, when the user blurs startDate call validateStartDate. In that function, use the endDate state value to validate.
See the pseudo code
<div>
<input type="text" name="startdate" value={this.state.startDate} onBlur={this.validateStartDate} />
<input type="text" name="enddate"value={this.state.endDate} onBlur={this.validateEndDate}/>
</div>
function validateStartDate(){
//check this.state.startDate < this.state.endDate
}
Or, you can use refs to get the endDate value.
<div>
<input type="text" ref="startDate" name="startdate" value={this.state.startDate} onBlur={this.validateStartDate} />
<input type="text" ref="endDate" name="enddate"value={this.state.endDate} onBlur={this.validateEndDate}/>
</div>
function validateStartDate(){
var startDate= ReactDOM.findDOMNode(this.refs.startDate).value
var endDate = ReactDOM.findDOMNode(this.refs.endDate).value
//check startDate < endDate
}
Hope this helps!
you can use refs supported by react.Check here for official documentation
<div>
<input type="text" name="startdate" ref= (input) => {this.startData = input.value; } value={this.state.startDate} onBlur={this.validateStartDate} />
<input type="text" name="enddate"value={this.state.endDate} onBlur={this.validateEndDate}/>
</div>
In validateEndDate function you can reference the ref.
validateEndDate(){
//you can use this.startDate here
}
NOTE: Refs do not work in stateless function components

Resources