I am appending a text box on click of Add New Candidate button and I also want to call validate the function of NewCandidate component on the click of save button I have tried with the following code but it's throwing an error if anybody knows the solution please answer.........................................................................................................................................
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Hello React!</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.7/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.7/react-dom.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.0-alpha1/JSXTransformer.js"></script>
<script src="https://www.gstatic.com/firebasejs/3.9.0/firebase.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/jsx">
class NewCandidate extends React.Component{
validate(){
alert()
}
render(){
return(
<table>
<tr>
<th colSpan="2">Candidate details</th>
</tr>
<tr>
<th>Name</th><td><input type="text" ref="candName" /></td>
</tr>
</table>
)
}
}
var CandidateList = [<NewCandidate />];
class Interview extends React.Component{
constructor(props){
super();
this.state={candidates:props.candidates}
}
updateCandidateList(newCandidate){
var updatedCandidates=this.state.candidates;
updatedCandidates.push(newCandidate);
this.setState({candidates: updatedCandidates})
}
render(){
return(
<div>
<Candidate candidates={this.state.candidates} />
<AddNewCandidate candidateList={this.updateCandidateList.bind(this)} />
</div>
)
}
}
class AddNewCandidate extends React.Component{
constructor(){
super()
}
addNewCandidate(e){
e.preventDefault();
this.props.candidateList(<NewCandidate />)
}
render(){
return(
<form>
<button onClick={this.addNewCandidate.bind(this)}>Add New Candidate</button>
<button type="button" onClick={NewCandidate.validate.bind(this)}>Save</button>
</form>
)
}
}
class Candidate extends React.Component{
constructor(props){
super(props);
}
render(){
var items=this.props.candidates.map((item)=>{
return (<div>{item}</div>)
});
return(
<div>
{items}
</div>
)
}
}
ReactDOM.render(<Interview candidates={CandidateList}/>,document.getElementById("root"));
</script>
</body>
</html>
Please check the following working snippet.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Hello React!</title>
<script src="https://unpkg.com/react#15/dist/react.js"></script>
<script src="https://unpkg.com/react-dom#15/dist/react-dom.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.min.js"></script>
<script src="https://www.gstatic.com/firebasejs/3.9.0/firebase.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
class NewCandidate extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
}
handleChange(e){
e.preventDefault();
this.props.handleCandidateChange(e.target.value, this.props.indexVal);
}
render(){
return(
<div>
<table>
<tbody>
<tr>
<th colSpan="2">Candidate details</th>
</tr>
<tr>
<th>Name</th><td><input type="text" onChange={this.handleChange}/></td>
</tr>
</tbody>
</table>
</div>
)
}
}
class Interview extends React.Component {
constructor(props){
super(props);
this.state = {
candidates: [],
values: []
}
this.addNewCandidate = this.addNewCandidate.bind(this);
this.handleSave = this.handleSave.bind(this);
this.handleCandidateChange = this.handleCandidateChange.bind(this);
}
handleCandidateChange(value, index) {
const newValues = [].concat(this.state.values);
newValues[index] = value;
this.setState({values: newValues});
}
handleSave(e) {
e.preventDefault();
this.state.values.forEach((val, index) => {
alert(`Validate ${index+1} value`);
})
}
addNewCandidate(e) {
e.preventDefault();
const candidateList = [].concat(this.state.candidates);
candidateList.push(<div key={`candidate-${candidateList.length}`}><NewCandidate indexVal={candidateList.length} handleCandidateChange={this.handleCandidateChange}/></div>)
this.setState({candidates: candidateList});
}
render() {
return (
<div>
{this.state.candidates}
<button type="button" onClick={this.addNewCandidate}>Add New Candidate</button>
<button type="button" onClick={this.handleSave}>Save</button>
</div>
)
}
}
// render Interview component
ReactDOM.render(<Interview />,document.getElementById("root"));
</script>
</body>
</html>
Here's an example. The main App component acts as a container for all relevant information and actions. We then use child components that are used for presenting data which will be using methods that have been passed down from the container to perform actions.
class NewCandidate extends React.Component {
state = {
name: ""
};
handleChange = evt => this.setState({
name: evt.target.value
});
addCandidate = () => {
const { name } = this.state;
if (name === "") {
return console.warn("input is empty");
}
return this.setState({
name: '',
}, () => this.props.add(name));
};
render() {
if (this.props.display) {
return (
<div>
<label>Name</label>
<input
type="text"
value={this.state.name}
onChange={this.handleChange}
/>
<button onClick={this.addCandidate}>Add Candidate</button>
</div>
);
}
return null;
}
}
const Candidate = ({ candidate }) => <div>{candidate.name}</div>;
class App extends React.Component {
state = {
showNewCandidateForm: false,
candidates: [
{
id: 1,
name: "Jeff"
},
{
id: 2,
name: "Andrew"
},
{
id: 3,
name: "Jess"
}
]
};
addCandidate = name => {
alert('validation here');
const { candidates } = this.state;
this.setState({
showNewCandidateForm: false,
candidates: [
...candidates,
{
id: candidates[candidates.length - 1].id + 1,
name,
}
]
});
};
showNewCandidateForm = () => this.setState({
showNewCandidateForm: true
});
hideNewCandidateForm = () => this.setState({
showNewCandidateForm: false
});
render() {
return (
<div>
<NewCandidate
display={this.state.showNewCandidateForm}
hide={this.hideNewCandidateForm}
add={this.addCandidate}
/>
{this.state.candidates.map(candidate => {
return <Candidate key={candidate.id} candidate={candidate} />;
})}
<button onClick={this.showNewCandidateForm}>New Candidate</button>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
Related
I am trying to learn vue.js version 3.0 and am experimenting with child components. I am using no-build vue.
I load an array and pass it to the child component and then clear the array in the child component but the length change is never reflected in the html. I was expecting it to go back to a length of zero.
Any help would be greatly appreciated.
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta charset="utf-8" />
<script src="https://unpkg.com/vue#next/dist/vue.global.prod.js"></script>
</head>
<body>
<div id="app">
<input type="button" v-on:click="LoadBoundaryList();" value="Add names" />
<test-component :names=userBoundaries></test-component>
</div>
<script>
const TestComponent = {
props: ['names'],
template: `<div>{{names.length}}</div><input type="button" value="remove" v-on:click="removeAllNames()"/>`,
methods: {
removeAllNames() {
this.names = [];
}
}
}
const RootComponent = {
data() {
return {
searchQuery: null,
userBoundaries: []
}
},
components: {
TestComponent
},
methods: {
LoadBoundaryList () {
for (var i = 0; i < 14; i++) {
this.userBoundaries.push("BoundaryName");
}
},
}
}
var appRoot = Vue.createApp(RootComponent);
appRoot.mount('#app');
</script>
</body>
</html>
You need to emit an event to the parent component to remove userBoundaries list, as name list is driven from it.
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta charset="utf-8" />
<script src="https://unpkg.com/vue#next/dist/vue.global.prod.js"></script>
</head>
<body>
<div id="app">
<input type="button" v-on:click="LoadBoundaryList();" value="Add names" />
<test-component :names="userBoundaries" #remove="remove"></test-component>
</div>
<script>
const TestComponent = {
props: ['names'],
template: `<div>{{names.length}}</div><input type="button" value="remove" v-on:click="removeAllNames()"/>`,
methods: {
removeAllNames() {
this.$emit('remove')
}
}
}
const RootComponent = {
data() {
return {
searchQuery: null,
userBoundaries: []
}
},
components: {
TestComponent
},
methods: {
LoadBoundaryList () {
for (var i = 0; i < 14; i++) {
this.userBoundaries.push("BoundaryName");
}
},
remove() {
this.userBoundaries = []
}
}
}
var appRoot = Vue.createApp(RootComponent);
appRoot.mount('#app');
</script>
</body>
</html>
Add emit from the child component to clear the parent bound data :
<html>
<head>
<title></title>
<meta charset="utf-8" />
<script src="https://unpkg.com/vue#next/dist/vue.global.prod.js"></script>
</head>
<body>
<div id="app">
<input type="button" v-on:click="LoadBoundaryList();" value="Add names" />
<test-component :names=userBoundaries #clear="clearNames"></test-component>
</div>
<script>
const TestComponent = {
props: ['names'],
template: `<div>{{names.length}}</div><input type="button" value="remove" v-on:click="removeAllNames()"/>`,
methods: {
removeAllNames() {
this.$emit('clear',[])
}
}
}
const RootComponent = {
data() {
return {
searchQuery: null,
userBoundaries: []
}
},
components: {
TestComponent
},
methods: {
LoadBoundaryList() {
for (var i = 0; i < 14; i++) {
this.userBoundaries.push("BoundaryName");
}
},
clearNames(arr){
this.userBoundaries=arr
}
}
}
var appRoot = Vue.createApp(RootComponent);
appRoot.mount('#app');
</script>
</body>
</html>
I'm trying to display my AboutPageContent.js to AboutPage.js. I would assume I would have to map it out somehow, but I'm not sure how.
Relevant Code
AboutPage.js
import React from 'react';
// CSS import statements
import '../css/AboutPage.css';
import '../css/App.css';
// Content import Statements
import AboutPageContent from '../content/AboutPageContent.js';
class About extends React.Component {
constructor(props) {
super(props);
this.state = AboutPageContent;
}
render() {
return(
<div className='about-page'>
<div className='page-header'>{this.state.about.name}</div>
<div>{this.state.about.desc.map(paragraph => <p>{paragraph}</p>)}</div>
</div>
)
}
}
export default About;
AboutPageContent.js
let AboutPageContent = {
about: [{
name: 'About Me',
desc: [
'p1',
'p2',
'p3',
'p4',
'p5',
'p6'
],
id: 1
}]};
export default AboutPageContent;
You have to do 2 maps, 1 for state.about and another one for state.about[i].desc.
class About extends React.Component {
constructor(props) {
super(props);
this.state = AboutPageContent;
}
render() {
return (
<div className="about-page">
<div className="page-header">{this.state.about.name}</div>
<div>
{this.state.about.map((currAbout) =>
currAbout.desc.map((paragraph, i) => <p key={i}>{paragraph}</p>)
)}
</div>
</div>
);
}
}
Or if you want to display the current about.name, move the <div className='page-header'>... inside this.state.about loop.
class About extends React.Component {
constructor(props) {
super(props);
this.state = AboutPageContent;
}
render() {
return (
<div className="about-page">
<div>
{this.state.about.map((currAbout, i) => (
<React.Fragment key={i}>
<div className="page-header">{currAbout.name}</div>
{currAbout.desc.map((paragraph, i) => (
<p key={i}>{paragraph}</p>
))}
</React.Fragment>
))}
</div>
</div>
);
}
}
Your this.state.about is an array, so you can't reference the name and desc properties with dot notation. Currently, this.state.about.name would be undefined. this.state.about[0].name would be equal to 'About Me'.
I would simply remove the [ ]s around the this.state.about property. That would make it an object (as opposed to an array) so the rest of your code should work fine. Leave this.state.about.desc as an array so you can use the map method.
You can access the object in about this way state.about[0]
const AboutPageContent = {
about: [
{
name: "About Me",
desc: ["p1", "p2", "p3", "p4", "p5", "p6"],
id: 1,
},
],
};
class About extends React.Component {
constructor(props) {
super(props);
this.state = AboutPageContent;
}
render() {
return (
<div className="about-page">
<div className="page-header">{this.state.about[0].name}</div>
<div>
{this.state.about[0].desc.map((paragraph) => (
<p>{paragraph}</p>
))}
</div>
</div>
);
}
}
ReactDOM.render(
<About />,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
However if you have more than one property in the object and you want to display description by name you could try this
const AboutPageContent = {
about: [
{
name: "About Me",
desc: ["p1", "p2", "p3", "p4", "p5", "p6"],
id: 1,
},
{
name: " Section 2",
desc: ["p12", "p10"],
id: 1,
},
],
};
class About extends React.Component {
state = AboutPageContent;
render() {
return (
<div className="about-page">
<div className="page-header">
{Object.values(this.state)
.flat()
.map((o, i) => (
<React.Fragment key={i}>
<p>{o.name}</p>
{o.desc.map((paragraph) => (
<p>{paragraph}</p>
))}
</React.Fragment>
))}
</div>
</div>
);
}
}
ReactDOM.render(
<About />,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
I working on a react app where I am displaying movies data. I want to
sort data based on Title from a-z and z-a by selecting from dropdown.
Check the below code. Unable to sort correctly with this approach. As I see
there is problem in onSorting
App.js -
class App extends Component {
constructor(props) {
super(props)
this.state = {
sort_term: '', movies: data.movies
}
this.onSorting = this.onSorting.bind(this);
}
onSorting(e) {
let term = e.target.value;
const sortedList = this.state.movies.sort((a, b) => {
if (a[term] > b[term]) return 1;
if (b[term] > a[term]) return -1;
return 0;
});
this.setState({ sort_term: term });
this.setState({ movies: sortedList });
}
render() {
const { movies } = this.state;
return (
<div className="App">
<Header sort_term={this.state.sort_term}
onSorting={this.onSorting} />
{movies.length === 0 ?
<h1>Loading Movies</h1> :
<Table movies={this.state.movies} />}
</div>
);
}
}
Header.js - here is the dropdown box with two options
class Header extends Component {
render() {
const { sort_term, onSorting } = this.props;
return (
<div className="nav">
<ul className="navLeft">
<li >
<form >
<select value={sort_term}
onChange={onSorting}
className="searchBar">
<option value="Title"> Sort (A - Z)</option>
<option value="Title"> Sort (Z - A) </option>
</select>
</form>
</li>
</ul>
</div>
);
}
}
Table.js -
class Table extends Component {
constructor(props) {
super(props);
this.state = {}
}
render() {
const list = this.props.movies.map((movie) => (
<tr key={movie.Title}>
<td>{movie.Title}</td>
<td>{movie.Director}</td>
</tr>
)
);
return (
<div className="table">
<table>
<thead>
<tr>
<th>Title</th>
<th>Director</th>
</tr>
</thead>
<tbody>
{list}
</tbody>
</table>
</div>
);
}
}
Actually, your code is working but since you sort A-Z you don't see the difference probably. Just change the sort function's related part like that:
if (a[term] > b[term]) return -1;
if (b[term] > a[term]) return 1;
Edit: If you have a non-sorted array your code should work. I tried it with a sorted list this is why I told you to change the order.
const movies = [
{ Title: "poo", Director: "poo" },
{ Title: "foo", Director: "bar" },
{ Title: "zoo", Director: "moo" },
]
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
sort_term: '', movies,
}
this.onSorting = this.onSorting.bind(this);
}
onSorting(e) {
let term = e.target.value;
const sortedList = [...this.state.movies].sort((a, b) => {
return a[term].localeCompare(b[term]);
});
this.setState({ sort_term: term });
this.setState({ movies: sortedList });
}
render() {
const { movies } = this.state;
return (
<div className="App">
<Header sort_term={this.state.sort_term}
onSorting={this.onSorting} />
{movies.length === 0 ?
<h1>Loading Movies</h1> :
<Table movies={this.state.movies} />}
</div>
);
}
}
class Header extends React.Component {
render() {
const { sort_term, onSorting } = this.props;
return (
<div className="nav">
<ul className="navLeft">
<li >
<form >
<select value={sort_term}
onChange={onSorting}
className="searchBar">
<option value="Title"> Sort (A - Z)</option>
<option value="Title"> Sort (Z - A) </option>
</select>
</form>
</li>
</ul>
</div>
);
}
}
class Table extends React.Component {
constructor(props) {
super(props);
this.state = {}
}
render() {
const list = this.props.movies.map((movie) => (
<tr key={movie.Title}>
<td>{movie.Title}</td>
<td>{movie.Director}</td>
</tr>
)
);
return (
<div className="table">
<table>
<thead>
<tr>
<th>Title</th>
<th>Director</th>
</tr>
</thead>
<tbody>
{list}
</tbody>
</table>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
But, you don't have a sort direction logic in your code. You are using the same sort function and it does a one-way sort right now.
Also, do not use the state directly like that since you are mutating it. Use concat or something like that:
const sortedList = [...this.state.movies].sort((a, b) => {
if (a[term] > b[term]) return -1;
if (b[term] > a[term]) return 1;
return 0;
});
Even better, you can use localCompare to make the sort more reliable. Thanks to #msbit for the comment and pointing out that.
const sortedList = [...this.state.movies].sort((a, b) => {
return a[term].localeCompare(b[term]);
});
I have a parent component that holds a number of children components. I want to add an active className to a a child component when it is clicked on.
This is working, but the issue is that each child component can have an active classname. Only one of the components should have be active each time.
Does anyone have any idea how to solve this issue?
Please see my code below.
class MyComponent extends Component {
constructor(props) {
super(props);
this.addActiveClass= this.addActiveClass.bind(this);
this.state = {
active: false,
};
}
addActiveClass() {
const currentState = this.state.active;
this.setState({ active: !currentState });
};
render() {
return (
<div
className={this.state.active ? 'active': null}
onclick={this.addActiveClass}
>
<p>{this.props.text}</p>
</div>
)
}
}
class Test extends Component {
render() {
return (
<div>
<MyComponent text={'1'} />
<MyComponent text={'2'} />
<MyComponent text={'3'} />
<MyComponent text={'4'} />
</div>
);
}
}
Add the active functionality to your Test Component.
There you can check whether there is already active Component or not.
class MyComponent extends React.Component {
render() {
return <div
id={this.props.id}
className={this.props.active ? 'active': null}
onClick={this.props.handleClick} >
{this.props.text}
</div>
}
}
class Test extends React.Component {
constructor(props) {
super(props);
this.state = {
components: [
{id: 1, text: 1},
{id: 2, text: 2},
{id: 3, text: 3},
{id: 4, text: 4}
],
activeID: null
};
}
handleClick(e) {
// If there is already active component ID, don't set another one!
// We support only one active ID.
if (this.state.activeID !== null) return;
const id = parseInt(e.target.id);
this.setState({
activeID: id
});
}
renderComponents() {
return (this.state.components.map( c =>
<MyComponent
id={c.id}
active={c.id === this.state.activeID}
text={c.text}
handleClick={this.handleClick.bind(this)} /> ));
}
renderActiveIDText() {
return (this.state.activeID ? <p>{"Active Component ID: " + this.state.activeID}</p> : null );
}
render() {
return <div>
{this.renderActiveIDText()}
{this.renderComponents()}
</div>
}
}
ReactDOM.render(<Test />, document.getElementById('container'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="container">
<!-- This element's contents will be replaced with your component. -->
</div>
I am repeating a OBJECT to render UI. Each row has a ItemName and a checkbox. Onclick of checkbox I should get the ID of that row. When I Run the page I am getting this error msg => Cannot read property 'addToCompare' of undefined
import React from 'react';
export default class List extends React.Component {
constructor(props) {
super(props);
}
addToCompare(event){
console.log('get id of row');
}
render() {
var planList = [
{id: 1, "itemname": "Sunshine},
{id: 2, "itemname": "Global"},
{id: 3, "itemname": "Lifetime"}
];
return (
<div id="layout-content" className="layout-content-wrapper">
{
planList.map(function(item, i) {
return (
<div className="row" key={i}>
<h1>
<input type="checkbox" onChange={this.addToCompare.bind(this)} />{item.name}
</h1>
</div>
)
})
}
)
}
Use es6 syntax, for map function to refer this to List as
planList.map((item, i) =>{
return (
<div className="row" key={i}>
<h1>
<input type="checkbox" onChange={this.addToCompare.bind(this)} />{item.name}
</h1>
</div>
)
})
or store this to other variable and use it to get addToCompare property as stated by #Marco
And If you're using Class declarations, you can use all ES6 features.
class Example extends React.Component {
constructor(props) {
super(props)
this.addToCompare = this.addToCompare.bind(this)
}
addToCompare(event) {
console.log('get id of row')
}
render() {
const planList = [
{ id: 1, itemname: 'Sunshine' },
{ id: 2, itemname: 'Global' },
{ id: 3, itemname: 'Lifetime' },
]
const rows = planList.map((item, i) =>
<div className="row" key={i}>
<h1>
<input type="checkbox" onChange={this.addToCompare} />
{item.itemname}
</h1>
</div>
)
return (
<div id="layout-content" className="layout-content-wrapper">
{rows}
</div>
)
}
}
ReactDOM.render(
<Example />,
document.getElementById('app')
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app" />
You are passing wrong context to the onChange method
class Example extends React.Component {
constructor(props) {
super(props);
}
addToCompare(event) {
console.log('get id of row');
}
render() {
var planList = [{
id: 1,
"itemname": "Sunshine"
}, {
id: 2,
"itemname": "Global"
}, {
id: 3,
"itemname": "Lifetime"
}, ];
var _this = this
var rows = planList.map(function(item, i) {
return (
<div className="row" key={i}>
<h1>
<input type="checkbox" onChange={_this.addToCompare.bind(_this)} />{item.itemname}
</h1>
</div>
)
}
)
return (
<div id="layout-content" className="layout-content-wrapper">
{rows}
</div>
)
}
}
ReactDOM.render(
<Example />,
document.getElementById('app')
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app" />
When you map the list to <div> tags using the map method, the value of this inside that mapping function is undefined, not the component, as it is invoked as an anonymous function.
Inside your render method, declare a that variable, to hold a reference to your component.
var planList = ... ,
that = this;
Now you can reference the component using that instead of this
<input type="checkbox" onChange={that.addToCompare.bind(that)} />{item.name}