React JS - Proper way to show the values from json array - reactjs

I have a requirement to fetch and show the values in the form text box. I need to check whether if the value exists and not equal to null then show the values in the text box otherwise show a blank field.
The existing code implementation shows something like this :
{
this.state.testJson.Names ? this.state.testJson.Names.length > 0 ? this.state.testJson.Names.map(response =>
<div className="form-group col-md-3" key={response.nameId}>
<label htmlFor="firstName">{Liferay.Language.get('first-name')}</label>
<input name="firstName" value={response.otherName} type="text" className="form-control" id="firstName" />
</div>
):
<div className="form-group col-md-3">
<label htmlFor="firstName">{Liferay.Language.get('first-name')}</label>
<input name="firstName" value='' type="text" className="form-control" id="firstName" />
</div> :
<div className="form-group col-md-3">
<label htmlFor="firstName">{Liferay.Language.get('first-name')}</label>
<input name="firstName" value='' type="text" className="form-control" id="firstName" />
</div>
}
I somehow feel this is not the best way to implement it as I need to avoid code repetition. Could someone tell me what is the better way to achieve this?
Thanks

There's a nice sintactic sugar for modern JavaScript and React (if you are using React, you must likely have it), you simply add a question mark before the object you're not sure it exists like:
this.state?.testJson?.Names?.length > 0
Also, you could have default values of a nullish variable like:
// names will be an empty array if it doesn't exist or if it's nullish
const names = this.state?.testJson?.Names ?? [];
All together is:
const names = this.state?.testJson?.Names ?? [];
return(
names.map(response =>
<div className="form-group col-md-3" key={response?.nameId}>
<label htmlFor="firstName">{Liferay.Language.get('first-name')}</label>
<input
name="firstName"
value={response?.otherName}
type="text"
className="form-control"
id="firstName"
/>
</div>
) : ....rest of the code!
);

You can fetching inside some cards
<div className="row">
{!names
? "Loading..."
: names.map((name) => {
return (
<div className="col">
<div className="card-body">
<h5 className="card-title">{name.firstname}</h5>
</div>
</div>
</div>
);
})}
</div>

Related

How to bind an object attribute in TS React

To introduce myself to React I am developing a small application - it is a 'media bookmark'. For example you really like a chapter from a certain book, so you give the application the name of the book, chapter, that it is a book, a description and a link if applicable.
The error I keep getting is:
Argument of type 'string' is not assignable to parameter of type 'SetStateAction<MediaBookmarkDTO>'
This is my code:
const [newBookmark, setNewBookmark] = useState<MediaBookmarkDTO>({ bookmarkName: '', bookmarkDescription: '', bookmarkType: '', bookmarkChapOrEp: '', bookmarkLink: '' });
And where I try to bind:
<div className="form-group col-md-4">
<label htmlFor="BookmarkName">Name:* </label>
<input type="text" className="form-control" id="BookmarkName" placeholder="Name"
value={newBookmark.bookmarkName} onChange={(e) => setNewBookmark(e.target.value)} />
</div>
Currently you are trying to update newBookmark with a string. Since a string isn't a MediaBookmarkDTO, you get an error. You probably meant to update the name only, which you can do inline like this:
<div className="form-group col-md-4">
<label htmlFor="BookmarkName">Name:* </label>
<input
type="text"
className="form-control"
id="BookmarkName"
placeholder="Name"
value={newBookmark.bookmarkName} onChange={(e) => setNewBookmark({
...newBookmark,
bookmarkName: e.target.value,
})}
/>
</div>
First of all welcome to stack overflow family.
newBookmark variable is MediaBookmarkDTO which is a object, when you try to update directly using setNewBookmark(e.target.value), it is trying to provide string value to newBookmark variable, which is where typescript is complaining.
When you are working with forms, in starting I would recommend to have separate state for each field it will help you understand more, when you got the base, then you can use single variable to store all form state. Below is an example to manage the form using a separate state.
import React, { useState } from 'react'
function BookmarkComponent() {
const [bookmarkName, setNewBookmarkName] = useState<string>('');
const [bookmarkDescription, setBookmarkDescription] = useState<string>('');
const [bookmarkType, setBookmarkType] = useState<string>('');
const [bookmarkChapOrEp, setBookmarkChapOrEp] = useState<string>('');
const [bookmarkLink, setBookmarkLink] = useState<string>('');
return (
<form>
<div className="form-group col-md-4">
<label htmlFor="BookmarkName">Name:* </label>
<input type="text" className="form-control" id="BookmarkName" placeholder="Name"
value={bookmarkName} onChange={(e) => setNewBookmarkName(e.target.value)} />
</div>
<div className="form-group col-md-4">
<label htmlFor="BookmarkDescription">Description:* </label>
<input type="text" className="form-control" id="BookmarkDescription" placeholder="Description"
value={bookmarkDescription} onChange={(e) => setBookmarkDescription(e.target.value)} />
</div>
<div className="form-group col-md-4">
<label htmlFor="BookmarkType">Type:* </label>
<input type="text" className="form-control" id="BookmarkType" placeholder="Type"
value={bookmarkType} onChange={(e) => setBookmarkType(e.target.value)} />
</div>
<div className="form-group col-md-4">
<label htmlFor="BookMarkChapter">Chapter:* </label>
<input type="text" className="form-control" id="BookMarkChapter" placeholder="Chapter"
value={bookmarkChapOrEp} onChange={(e) => setBookmarkChapOrEp(e.target.value)} />
</div>
<div className="form-group col-md-4">
<label htmlFor="BookmarkLink">Link:* </label>
<input type="text" className="form-control" id="BookmarkLink" placeholder="Link"
value={bookmarkLink} onChange={(e) => setBookmarkLink(e.target.value)} />
</div>
</form>
)
}
export default BookmarkComponent
When You get more experience you can use Libraries to manage form they are extremely Helpful when managing complex form, below are libraries I used which works very well
React Hook Form
Formik

how to insert data as an array of objects in React JS

my question is a little complicated, I am building a trip-related web application where users can book trips. So I have made a function that increases the number of travelers as the user clicks the + sign. when this function is called it changes the state and another function gets triggered that displays the form to fill in the traveler details. Now this form is rendered according to the number of travelers traveling. how can I set that data in an array of objects?
here's a screenshot guide:
I want the data to be in the state like this:
travelersDetail: [{firstName: 'Farrukh', lastName:'Ayaz', address:'...', city:'Lahore'},
{firstName: 'Dwight', lastName:'Schrute', address:'...', city:'Scranton'},
{firstName: 'Micheal', lastName:'Scott', address:'...', city:'Scranton'},]
My code:
// state
state = {
NumOfTravellers : 1,
travelersDetail: [],
trip: null,
}
// the functions that increases the number of travelers
handleClick = (e) =>{
e.preventDefault();
if(e.target.id == 'plus'){
this.setState({NumOfTravellers: this.state.NumOfTravellers + 1 })
}
else if(e.target.id == 'minus'){
this.state.NumOfTravellers > 1 ? this.setState({NumOfTravellers:
this.state.NumOfTravellers - 1 }) : alert("can't be less than that :)")
}
}
// the function that returns the traveler details form, according to the number of travelers traveling.
const numberOfTravelers = () =>{
var travellers = [];
for(let t = 0; t < this.state.NumOfTravellers; t++){
travellers.push(
<div >
<h4> Traveller # {t+1} Details</h4><br/>
<div className="form-row">
<div className="form-group col-md-6">
<label htmlFor="firstName">First Name</label>
<input type="firstName" className="form-control" onChange={this.handleTDChange} id="firstName" placeholder="FirstName" />
</div>
<div className="form-group col-md-6">
<label htmlFor="lastName">Last Name</label>
<input type="lastName" className="form-control" onChange={this.handleTDChange} id="lastName" placeholder="LastName" />
</div>
</div>
<div className="form-group">
<label htmlFor="address">Address</label>
<input type="text" className="form-control" onChange={this.handleTDChange} id="address" placeholder="1234 Main St" />
</div>
<div className="form-group">
<label htmlFor="phoneNumber">Phone Number</label>
<input type="tel" className="form-control" onChange={this.handleTDChange} id="phoneNumber" placeholder="+92..." />
</div>
<div className="form-row">
<div className="form-group col-md-6">
<label htmlFor="city">City</label>
<select onChange={this.handleTDChange} id="city" className="form-control">
<option selected>Choose...</option>
<option>Lahore</option>
<option>Islamabad</option>
<option>Karachi</option>
<option>Rawalpindi</option>
<option>Quetta</option>
<option>Multan</option>
</select>
</div>
<div className="form-group col-md-4">
<label htmlFor="state">State</label>
<select onChange={this.handleTDChange} id="state" className="form-control">
<option selected>Choose...</option>
<option>Pakistan</option>
</select>
</div>
<div className="form-group col-md-2">
<label htmlFor="zip">Zip</label>
<input type="text" className="form-control" onChange={this.handleTDChange} id="zip" />
</div>
</div>
</div>);
}
return travellers
}
I don't completely understand you problem, what I understand is.
There is a controller, Plus and Minus. On click of Plus a new Traveler form has to be added and on click of minus the last Travelers form will be removed. And also the traveler counter is incremented or decremented based on the button click
You would not want 2 variables, 1 to keep track of the number of travelers and other to store the traveler details, you can maintain only 1 variable. Just have traverlerDetails, we can get the number of travelers form the size of the traverlerDeterails array.
// state values
this.state = {
travelersDetail: [],
trip: null,
};
handleClick = (clickEvent) => {
clickEvent.preventDefault();
const travelersDetailCopy = [...this.state.travelersDetail];
if (e.target.id == 'plus') {
travelersDetailCopy.push({
firstName: '', lastName: '', address: '', city: '' // Add empty data
});
} else if (e.target.id == 'minus') {
if (this.state.travelersDetail.length === 1) {
alert("Can't be less than 1");
} else {
travelersDetailCopy.pop();
}
}
this.setState({
travelersDetail: travelersDetailCopy
});
}
const numberOfTraverlers = () => {
return this.state.travelersDetail.map((travelerDetails, index) => {
return (
<div key={index}>
<h4> Traveller # {index + 1} Details</h4><br />
<div className="form-row">
<div className="form-group col-md-6">
<label htmlFor="firstName">First Name</label>
<input type="firstName" className="form-control" onChange={(event) => {this.handleTDChange(event, index, "firstName")}} id="firstName" placeholder="FirstName" />
</div>
<div className="form-group col-md-6">
<label htmlFor="lastName">Last Name</label>
<input type="lastName" className="form-control" onChange={(event) => {this.handleTDChange(event, index, "lastName")}} id="lastName" placeholder="LastName" />
</div>
</div>
<div className="form-group">
<label htmlFor="address">Address</label>
<input type="text" className="form-control" onChange={(event) => {this.handleTDChange(event, index, "address")}} id="address" placeholder="1234 Main St" />
</div>
<div className="form-group">
<label htmlFor="phoneNumber">Phone Number</label>
<input type="tel" className="form-control" onChange={(event) => {this.handleTDChange(event, index, "phoneNumber")}} id="phoneNumber" placeholder="+92..." />
</div>
<div className="form-row">
<div className="form-group col-md-6">
<label htmlFor="city">City</label>
<select onChange={(event) => {this.handleTDChange(event, index, "city")}} id="city" className="form-control">
<option selected>Choose...</option>
<option>Lahore</option>
<option>Islamabad</option>
<option>Karachi</option>
<option>Rawalpindi</option>
<option>Quetta</option>
<option>Multan</option>
</select>
</div>
<div className="form-group col-md-4">
<label htmlFor="state">State</label>
<select onChange={(event) => {this.handleTDChange(event, index, "state")}} id="state" className="form-control">
<option selected>Choose...</option>
<option>Pakistan</option>
</select>
</div>
<div className="form-group col-md-2">
<label htmlFor="zip">Zip</label>
<input type="text" className="form-control" onChange={(event) => {this.handleTDChange(event, index, "zip")}} id="zip" />
</div>
</div>
</div>
)
})
}
handleTDChange(event, index, updateField) {
const arrayCopy = [...this.state.travelersDetail];
arrayCopy[index][updateField] = event.target.value;
this.setState({travelersDetail: arrayCopy});
}
Use this.state.travelersDetail.length to display the number of travelers.
Don't use for-loop, make use of built in functions like forEach, map, filter and other methods.
Update :
To handle onChange events, you can have multiple handleChange event handler.
But if you want to do it in a single, you can pass few additional argument. First being the actual event, second the index of the travelerDetails object, third being the property that needs to be updated.
There is a much better way of doing this, extract the content in side the map and create a separate component. Which would contain the logic related to the component. With this updation and also maintenance of the code is much easier
You should be using the array.push() method detailed in javascript to add an element to an existing array.
Example
const array = [];
array.push({ id: 'someId', name: 'someName' });
See documentation: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/push

Error during checked the box with condition

Code doesnot have any error but I cant Able to load the check boxes. Iam using nested loop to cheeck the condition if tag.id== taged.id then i need to make itchecked.What to do?
<div className="form-group row">
<label for="inputEmail3" className="col-sm-2 col-form-label">
TAG
</label>
<div className="col-sm-10">
<div className="checkbox">
{tate.tags.map(
(tag) =>
hiding.hide &&
form.tags.map((taged) => (
<label>
<input
name="tag[]"
type="checkbox"
value={tag.id}
onChange={handlecheck}
checked={tag.id == taged.id ? true : false}
/>
namnemnma {tag.Tag_name}
</label>
))
)}
</div>
</div>
</div>

React Select Always re-render, even when change another state

React Select Always re-render, even when change another state.
...
const [expense_currency, setExpenseCurrency] = React.useState(expense_claim.expense_currency);
const [remarks, setRemarks] = React.useState(expense_claim.remarks);
...
...
return (
...
<div className="form-group row p-0 col-sm-12 col-md-6">
<label className="text-nowrap col-form-label col-sm-4">Currency</label>
<div className="col-sm-8 p-0" title={expense_currency}>
<Select
className="select2"
placeholder="Select Currency"
isDisabled={false}
isClearable={true}
value={expense_currency}
options={select_currency}
onChange={selected => { setExpenseCurrency(selected.value) }}
/>
</div>
</div>
<div className="form-group row p-0 col-sm-12 col-md-6">
<label className="text-nowrap col-form-label col-sm-4">
Remarks
</label>
<div className="col-sm-8 p-0">
<textarea
className="form-control"
required={true}
disabled={false}
name="remarks"
placeholder=""
defaultValue={remarks}
onChange={e => setRemarks(e.target.value)}
></textarea>
</div>
</div>
)
SS from my chrome react profiler
i have try react-select v2.0.0 to v.2.4.4 all have same result.
can i know how to make it not rerender when i'm update my remarks fields ?
This depends on whether Select is using PureComponent or memo() or something similar. But if it is the props you pass in will break the check every time since you are creating a callback for onChange every render.
To have a consistent callback reference use useCallback:
const onSelectChange = useCallback(selected => setExpenseCurrency(selected.value), [setExpenseCurrency]);
And pass it in:
onChange={onSelectChange}
If select_currency is a non-primitive e.g. array or object you will need to ensure that is a consistent reference too. You didn't post that so can't advise.

How to display error message when passwords doesn't match in Angularjs?

I'm new at Angularjs and my question is how to display an error message when the password doesn't match with confirm password?
Can someone help me, this is not very difficult but I'm still learning to programme.
Thanks to everyone!
I have html code:
<form ng-submit="saveItem(userForm.$valid)" name="userForm">
<div class="row">
<div class="col-sm-6">
<div class="form-group">
<label for="database_address">User</label>
<input type="text" class="form-control" required ng-model="activeItem.username" placeholder="Потребителско Име..." />
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="text" class="form-control" id="password" ng-model="activeItem.passwordString" />
</div>
<div class="form-group">
<label for="password">Confirm Password</label>
<input type="text" class="form-control" id="password" ng-model="activeItem.passwordConfirm" />
</div>
<p ng-show="(userForm.passwordConfirm != '') && (userForm.password != userForm.passwordConfirm)">Passwords don't match</p>
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="username">Operator</label>
<input type="text" class="form-control" required id="username" ng-model="activeItem.name" />
</div>
</div>
</div>
<button class="btn btn-primary" ng-disabled="userForm.$invalid" type="submit">Save</button>
<!--<button class="btn btn-primary" ng-disabled="userForm.$invalid" type="submit">Добавяне на нов</button>-->
</form>
And angular function:
$scope.saveItem = function(){
console.log($scope.activeItem);
//delete $scope.activeItem.hash_method
var objectToSave = {
username: $scope.activeItem.username,
//password: $scope.activeItem.password,
name: $scope.activeItem.name,
id: $scope.activeItem.id
};
if($scope.activeItem.passwordString != ''){
if($scope.activeItem.passwordString == $scope.activeItem.passwordConfirm){
objectToSave.password = $scope.activeItem.passwordString;
} else {
console.log('Confirm password error');
}
}
You'll want to keep different Id's for the two password fields, also take a look at your model bindings:
<input type="text" class="form-control" id="password" ng-model="activeItem.passwordString" />
<input type="text" class="form-control" id="passwordConfirm" ng-model="activeItem.passwordConfirm" />
You can just reference the items that are bound with ng-model within an ng-if/ng-show, and then you shouldn't need any custom logic on the back-end.
<p ng-show="(activeItem.passwordString && activeItem.passwordConfirm) && activeItem.passwordString
!== activeItem.passwordConfirm ">Passwords don't match</p>
Also, you'll probably want to use '!==' over '!=' since you're just comparing two strings, as it's more strict of a comparison.
Edit: one thing to note, with this direction you'll still probably want to do error checking in the save function, but this should handle displaying the error message without any issues.
Remember the operator for 'not equal' is "!==", with that you will be able to make it!

Resources