React render function within a function in JSX? - reactjs

In my component's render function I'm calling anouther function with markup. So far so good.
renderEquipmentOptions() {
return (
<div className="form-check">
<label className="form-check-label">
<input className="form-check-input" type="checkbox" value=""/>
Nothing
</label>
</div>
)
}
However now I want to add a map to this function to generate additional markup.
renderEquipmentOptions() {
const render = this.props.equipment.map((item, i) => {
return (
<div key={i} className="form-check form-check-inline">
<label className="form-check-label">
<input className="form-check-input" type="checkbox" value=""/>
{item}
</label>
</div>
)
});
return (
<div className="form-check">
<label className="form-check-label">
<input className="form-check-input" type="checkbox" value=""/>
Nothing
</label>
</div>
{this.render()}
)
}
This doesn't work. It seems you cant wrap a function in {} and call it. Is this because its a function within a function?

It doesn't work because you are trying to return multiple elements from the function without returning them as an array. Also render is not a function in your case but a variable
change your code to return an array like
renderEquipmentOptions() {
const render = this.props.equipment.map((item, i) => {
return (
<div key={i} className="form-check form-check-inline">
<label className="form-check-label">
<input className="form-check-input" type="checkbox" value=""/>
{item}
</label>
</div>
)
});
return (
[<div className="form-check">
<label className="form-check-label">
<input className="form-check-input" type="checkbox" value=""/>
Nothing
</label>
</div>,
...render
]
)
}

Related

trigger functions with radio buttons React/TS

I have radio buttons that look like this:
<div className="row">
<div className="col-md-4">
<label className="radio">
<input onChange={() => {serviceCalc()}} type="radio" name="radio-btn-product" value="standard" id="standard" checked />
<i></i>Standard
</label>
</div>
<div className="col-md-4">
<label className="radio">
<input onChange={() => {serviceCalc()}} type="radio" name="radio-btn-product" value="premium" id="premium" />
<i></i>Premium
</label>
</div>
<div className="col-md-4">
<label className="radio">
<input onChange={() => {serviceCalc()}} type="radio" name="radio-btn-product" value="excelium" id="excelium" />
<i></i>Excelium
</label>
</div>
</div>
I'm trying to get the serviceCalc function to trigger the three other functions depending on which radio button is clicked like so:
const serviceCalc = () => {
const standard1 = (document.getElementById("standard") as HTMLInputElement);
const premium1 = (document.getElementById("premium") as HTMLInputElement);
const excelium1 = (document.getElementById("excelium") as HTMLInputElement);
if (standard1.checked){
standard();
}
else if (premium1.checked) {
premium();
}
else if (excelium1.checked) {
excelium();
}
}
but when I select the standard option for example, this wont trigger:
const standard = () => {
console.log('stand_test')
}
any help is greatly appreciated.
The isssue is that your radio buttons are controlled (you have an onChange on them and you have checked on one of them), but you're only ever setting checked on standard. Since standard is always checked, clicking standard isn't changing anything, so the change event isn't raised.
Separately, onChange={() => {serviceCalc()}} should almost always be onChange={serviceCalc}.
Normally, when you're using controlled inputs, you store the state in the component state. Here's an example:
const { useState } = React;
const Example = () => {
const [serviceLevel, setServiceLevel] = useState("standard");
const serviceCalc = ({currentTarget: {value}}/*: {currentTarget: HTMLInputElement}*/) => {
setServiceLevel(value);
// If you need to run other functions here, you can do that
// with an `if`/`else`/etc. on `value`, which is
// `"standard"`, `"premium"`, or `"excelium"
};
// This is purely to show the new service level
console.log(`service level is ${serviceLevel}`);
return (
<div className="row">
<div className="col-md-4">
<label className="radio">
<input onChange={serviceCalc} type="radio" name="radio-btn-product" value="standard" checked={serviceLevel === "standard"} />
<i></i>Standard
</label>
</div>
<div className="col-md-4">
<label className="radio">
<input onChange={serviceCalc} type="radio" name="radio-btn-product" value="premium" checked={serviceLevel === "premium"} />
<i></i>Premium
</label>
</div>
<div className="col-md-4">
<label className="radio">
<input onChange={serviceCalc} type="radio" name="radio-btn-product" value="excelium" checked={serviceLevel === "excelium"} />
<i></i>Excelium
</label>
</div>
</div>
);
};
ReactDOM.render(<Example />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script>
Pass a parameter depending on which button was checked:
const serviceCalc = (service: "standard" | "premium" | "excelium") => {
if (service === "standard") {
return standard();
}
if (service === "premium") {
return premium();
}
if (service === "excelium") {
return excelium();
}
}
And you would use it like so:
<div className="col-md-4">
<label className="radio">
<input onChange={() => {serviceCalc("excelium")}} type="radio" name="radio-btn-product" value="excelium" id="excelium" />
<i></i>Excelium
</label>
</div>

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

Showing input value in the url link (ReactJs Laravel)

I created laravel project with the reactjs framework and I'm new for this framework. I have problem and why It happens every time i submit the form.
Goal: users can register through online
Problem:
Why it happens when i submit the button the input value of user shown in the url link?
The data that I input is not inserted to the database.
Code:
constructor() {
super();
this.state = {
f_name:'',
l_name:'',
m_name:'',
email:'',
home_num:'',
contact_num:'',
Job_name:[],
employ_status:'',
employ_relocate:'',
employ_start_date:'',
employ_file:''
}
this.handleSubmit = this.handleSubmit.bind(this);
this.handle_fname = this.handle_fname.bind(this);
this.handle_lname = this.handle_lname.bind(this);
this.handle_mname = this.handle_mname.bind(this);
this.handle_email = this.handle_email.bind(this);
this.handle_homenum = this.handle_homenum.bind(this);
this.handle_contactnum = this.handle_contactnum.bind(this);
this.handle_employ_status = this.handle_employ_status.bind(this);
this.handle_employ_relocate = this.handle_employ_relocate.bind(this);
this.handle_employ_start_date = this.handle_employ_start_date.bind(this);
this.handle_employ_file = this.handle_employ_file.bind(this);
}
componentDidMount() {
const id = this.props.match.params.id;
axios.get('/api/online_application_job_title/' +id).then(response => {
this.setState({
Job_name:response.data
})
})
}
handleSubmit(e)
{
const data = {
firstname: this.state.f_name,
lastname : this.state.l_name,
middlename : this.state.m_name,
email : this.state.email,
home_number : this.state.home_num,
contact_num : this.state.contact_num,
job : this.state.Job_name[0].position_name,
employ_status : this.state.employ_status,
employ_relocate : this.state.employ_relocate,
employ_start_date : this.state.employ_start_date,
employ_file : this.state.employ_file
}
axios.post('/api/save_application',data).then(response => {
console.log(response);
}).catch(error => console.log(error));
}
handle_fname(e)
{
this.setState({
f_name:e.target.value,
})
}
handle_lname(e){
this.setState({
l_name:e.target.value,
})
}
handle_mname(e){
this.setState({
m_name:e.target.value,
})
}
handle_email(e){
this.setState({
email:e.target.value,
})
}
handle_homenum(e){
this.setState({
home_num:e.target.value
})
}
handle_contactnum(e){
this.setState({
contact_num:e.target.value
})
}
handle_employ_status(e){
this.setState({
employ_status:e.target.value
});
}
handle_employ_relocate(e){
this.setState({
employ_relocate:e.target.value,
})
}
handle_employ_start_date(e){
this.setState({
employ_start_date:e.target.value,
})
}
handle_employ_file(e){
this.setState({
employ_file: e.target.files[0].extension
})
}
renderName() {
return (
this.state.Job_name.map(name => (
<input placeholder="" value={name.position_name} type="text" className="form-control"/>
))
)
}
render() {
return (
<div>
<div className="header">
<div className="jumbotron">
<h1>Online Application</h1>
</div>
</div>
<form onSubmit={this.handleSubmit}>
<div className="container">
<h5><b>Personal Info</b></h5>
<br/>
<div className="row">
<div className="col-md-6">
<input
placeholder="First Name*"
value={this.state.f_name}
onChange={this.handle_fname}
className="form-control"/>
</div>
<div className="col-md-6">
<input
placeholder="Last Name*"
value={this.state.l_name}
onChange={this.handle_lname}
className="form-control"/>
</div>
</div>
<br/>
<div className="row">
<div className="col-md-6">
<input
placeholder="Middle Name*"
value={this.state.m_name}
onChange={this.handle_mname}
className="form-control"/>
</div>
<div className="col-md-6">
<input
placeholder="Email Address*"
type="email"
value={this.state.email}
onChange={this.handle_email}
className="form-control"/>
</div>
</div>
<br/>
<div className="row">
<div className="col-md-6">
<input
placeholder="Home Number*"
type="number"
value={this.state.home_num}
onChange={this.handle_homenum}
className="form-control"/>
</div>
<div className="col-md-6">
<input
placeholder="Contact Number*"
type="number"
value={this.state.contact_num}
onChange={this.handle_contactnum}
className="form-control"/>
</div>
</div>
<br/><br/>
<h5><b>Employment Application</b></h5>
<br/>
<div className="row">
<div className="col-md-6">
<p>Position Applying For</p>
{this.renderName()}
</div>
<div className="col-md-6">
</div>
</div>
<br/><br/>
<div className="row">
<div className="col-md-6">
<p>1. What is your current employment status?</p>
<div className="form-check-inline">
<label className="form-check-label">
<input
type="radio"
className="form-check-input"
name="employmentstatus"
onChange={this.handle_employ_status}
defaultChecked={false}
value="Unemployed"/>Unemployed
</label>
</div>
<div className="form-check-inline">
<label className="form-check-label">
<input
type="radio"
className="form-check-input"
name="employmentstatus"
onChange={this.handle_employ_status}
defaultChecked={false}
value="Employed"/>Employed
</label>
</div>
<div className="form-check-inline disabled">
<label className="form-check-label">
<input
type="radio"
className="form-check-input"
name="employmentstatus"
onChange={this.handle_employ_status}
defaultChecked={false}
value="Self-Employed"/>Self-Employed
</label>
</div>
<div className="form-check-inline disabled">
<label className="form-check-label">
<input
type="radio"
className="form-check-input"
name="employmentstatus"
onChange={this.handle_employ_status}
defaultChecked={false}
value="Student"/>Student
</label>
</div>
</div>
<div className="col-md-6"></div>
</div>
<br/>
<div className="row">
<div className="col-md-6">
<p>2. Are you willing to relocate?</p>
<div className="form-check-inline">
<label className="form-check-label">
<input type="radio"
name="relocate"
onChange={this.handle_employ_relocate}
className="form-check-input"
value="Yes"/>Yes
</label>
</div>
<div className="form-check-inline">
<label className="form-check-label">
<input type="radio"
name="relocate"
onChange={this.handle_employ_relocate}
className="form-check-input"
value="No"/>No
</label>
</div>
</div>
<div className="col-md-6"></div>
</div>
<br/>
<div className="row">
<div className="col-md-6">
<p>3. When is your available start date?</p>
<input
name="startdate"
type="date"
onChange={this.handle_employ_start_date}
value={this.state.employ_start_date}
required=""
className="form-control"/>
</div>
<div className="col-md-6"></div>
</div>
<br/>
<div className="row">
<div className="col-md-6">
<p>4. Kindly attach a copy of your resume (PDF,docx files only).</p>
<div className="custom-file">
<input
type="file"
name="file"
accept="application/msword,application/pdf"
onChange={this.handle_employ_file}
className="custom-file-input"
id="inputGroupFile04"/>
<label className="custom-file-label" htmlFor="inputGroupFile04">Choose file</label>
</div>
</div>
<div className="col-md-6"></div>
</div>
<br/>
<div className="row">
<div className="col-md-6">
<input
className="btn btn-outline-primary btn-large form-control col-md-5"
type="submit"
value="Send Application"/>
</div>
<div className="col-md-6"></div>
</div>
</div>
</form>
</div>
)
}
Controller:
public function save_application(Request $request)
{
$firstname = $request->get('firstname');
$lastname = $request->get('lastname');
$middlename = $request->get('middlename');
$email = $request->get('email');
$home_number = $request->get('home_number');
$contact_num = $request->get('contact_num');
$job = $request->get('job');
$employ_status = $request->get('employ_status');
$employ_relocate = $request->get('employ_relocate');
$employ_start_date = $request->get('employ_start_date');
$employ_file = $request->get('employ_file');
$now = new DateTime();
DB::insert('INSERT INTO onlineapplication
(position_name,firstname,middlename,lastname,email,homenumber,phonenumber,employmentstatus,relocate,starting_date,destination,file_img_name,Status)
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)',[
$firstname,
$lastname,
$middlename,
$email,
$home_number,
$contact_num,
$job,
$employ_status,
$employ_relocate,
$employ_start_date,
$employ_file
]);
return response()->json('Successfully inserted');
}
When form tag is used, the submit will trigger the default behaviour that is based on the method provided and the action url.
as in your example you are handling the data explicitly you should prevent the default behaviour.
add the below code in handle submit
handleSubmit(e) {
e.preventDefault();
...
...
}
this will prevent the default behaviour.
Improvement for state update:
you don't need individual functions to update the input value to state this can be combined in one function.
to combine, provide the input name same as state name.
this.state ={
"f_name": '',
"l_name": '',
...
}
<input name="f_name" ... onChange={this.handleInputChange}/>
<input name="l_name" .. onChange={this.handleInputChange}/>
handleInputChange(e){
let target = e.target;
let name = target.name;
let value = target.value
this.setState({[name]: value})
}
for more info refer this link.
First, I just want to introduce to you to the arrow function in JavaScript (ES6). Declaring private methods like this:
handle_fname = (e) =>
{
this.setState({
f_name:e.target.value,
})
}
will save you time from unnecessary binding in the constructor.
Regarding your question, you missed to show the content of your this.handleSubmit(). Without this, I can assume that the form submit fired a get call since you failed to put method attribute in your <form/> tag, and without indicating your method attribute will result to default get method. Get method when used, data submitted will be visible in the page address field of your browser.
EDIT
You already added your handleSubmit() in your question, and it looks okay. If data is still shown in the address field of your browser, try adding method="post" in your form tag like this:
<form onSubmit={this.handleSubmit} method="post">

How to iterate over DOM?

How to iterate over DOM element inputbox. I want to loop through each <input/>. I get error this.refs.inputBoxes.map is not a function
componentDidMount: function(){
this.refs.inputBoxes.map(function(e, i){
console.log(e)
})
render: function () {
return (
<div>
<input className= "iB" type="checkbox" ref="inputBoxes"/>
<input className= "iB" type="checkbox" ref="inputBoxes"/>
<input className= "iB" type="checkbox" ref="inputBoxes"/>
</div>
)
}
I think you are simply overriding the previously set this.refs.inputBoxes field. I don't think that automatically becomes an array if you set the same ref for multiple components. However you could give a ref to your container:
<div ref={e => this._inputBoxContainer = e}>
<input className= "iB" type="checkbox"/>
<input className= "iB" type="checkbox"/>
<input className= "iB" type="checkbox"/>
</div>
And in your mount method simply access the children! If you want to perform array operations on it you will have to convert it to an array since it's an HtmlCollection.:
componentDidMount() {
Array.from(this._inputBoxContainer.children).map(e => {
console.log(e)
})
}
There's really no reason to not just use document.querySelectorAll
componentDidMount: function() {
var inputs = document.querySelectorAll('input.iB');
for (var i = 0; i < inputs.length; i++) {
console.log(inputs[i]);
}
}
render: function () {
return (
<div>
<input className= "iB" type="checkbox" ref="inputBoxes"/>
<input className= "iB" type="checkbox" ref="inputBoxes"/>
<input className= "iB" type="checkbox" ref="inputBoxes"/>
</div>
)
}

Trying to reset 2 different ng-model on 2 different input file type in angularjs

I am new to angular i'm trying to clear the model on click of cross image
<div class="content">
<div class="field">
<label for="form-bridge-logo1">Logo 1</label>
<input id="form-bridge-logo1" type="text" value="" ng-model="myFile.name" />
<button class="clear-file" ng-if="myFile.name" ng-click="clear(myFile)"></button>
<input type="file" id="browsefile" name="cover" file-browser="myFile" data-logo-type="primary" class="browse-file-input" accept="image/jpg, image/jpeg, image/png"/>
<label for="browsefile" class="browse-btn">Browse</label>
</div>
</div>
<div class="content">
<div class="field">
<label for="form-bridge-logo2">Logo 2</label>
<input id="form-bridge-logo2" type="text" ng-model="myFile1.name"/>
<button class="clear-file" ng-if="myFile1.name" ng-click="clear(myFile1)"></button>
<input type="file" id="filebrowse" name="cover" file-browser="myFile1" data-logo-type="secondary" class="browse-file-input" accept="image/jpg, image/jpeg, image/png"/>
<label for="filebrowse" class="browse-btn">Browse</label>
</div>
</div>
and this is my js file.
$scope.clear = function(fileName) {
if (fileName.name === $scope.myFile.name && $scope.myFile !== undefined) {
$scope.myFile = $scope.initial;
} else if (fileName.name === $scope.myFile1.name) {
$scope.myFile1 = $scope.initial;
}
}
you can just do that:
<button class="clear-file" ng-if="myFile.YOURDATA" ng-click="myFile.YOURDATA=''">Clear</button>
So you don't need an extra function

Resources