What I want to do is to change the border on the input to red if the input value doesn't match any movie in the API call. The user types in the input field and the call to the API shows the matching result. If we don't have any result I would like the border on the input to be red. But I can't see how I should make that happen.
The ponent Input is at the end of the code snippet.
CSS
.input-style {
padding: 7px;
border-radius: 5px;
border: 1px solid #cccccc;
font-family: Courier New, Courier, monospace;
transition: background-color 0.3s ease-in-out;
outline: none;
}
.input-style:focus {
border: 1px solid turquoise;
}
APP.js
class App extends Component {
constructor() {
super();
this.state = {
value: '',
items: [],
isLoading: false,
searchResult: null,
error: false,
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
// To handle search
handleChange(e) {
this.setState({ value: e.target.value });
}
handleSubmit(e) {
let searchResult = [];
for (var i = 0; i < this.state.items.length; i++) {
if (
this.state.items[i].name
.toLowerCase()
.indexOf(this.state.value.toLowerCase()) !== -1
) {
searchResult.push(this.state.items[i]);
} else {
console.log('No matches on your search, try again');
}
}
e.preventDefault();
// If we have something in the object searchResult
if (searchResult.length > 0) {
this.setState({
error: false,
value: '',
searchResult: searchResult,
});
} else {
this.setState({
error: true,
value: '',
searchResult: [],
});
}
}
// call to the API
ponentDidMount() {
this.setState({ isLoading: !this.state.isLoading });
fetch('')
.then(response => response.json())
.then(data => {
this.setState({
items: data,
error: false,
});
this.setState({ isLoading: !this.state.isLoading });
})
.catch(console.error);
}
render() {
return (
<div className="App">
<Header />
<Loader isLoading={this.state.isLoading} />
<Input
handleChange={this.handleChange}
handleSubmit={this.handleSubmit}
value={this.state.value}
/>
{this.state.error ? (
<p className="errorMsg">No match on the search, try again!</p>
) : null}
<Search search={this.state.searchResult} />
</div>
);
}
}
export default App;
Input.js
function Input(props) {
return (
<div>
<form onSubmit={props.handleSubmit}>
<input
type="text"
className="input-style"
placeholder="Sök efter film.."
value={props.value}
onChange={props.handleChange}
/>
<button id="bold" className="button-style" type="submit">
<i className="fa fa-search" />
</button>
</form>
</div>
);
}
export default Input;
What I want to do is to change the border on the input to red if the input value doesn't match any movie in the API call. The user types in the input field and the call to the API shows the matching result. If we don't have any result I would like the border on the input to be red. But I can't see how I should make that happen.
The ponent Input is at the end of the code snippet.
CSS
.input-style {
padding: 7px;
border-radius: 5px;
border: 1px solid #cccccc;
font-family: Courier New, Courier, monospace;
transition: background-color 0.3s ease-in-out;
outline: none;
}
.input-style:focus {
border: 1px solid turquoise;
}
APP.js
class App extends Component {
constructor() {
super();
this.state = {
value: '',
items: [],
isLoading: false,
searchResult: null,
error: false,
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
// To handle search
handleChange(e) {
this.setState({ value: e.target.value });
}
handleSubmit(e) {
let searchResult = [];
for (var i = 0; i < this.state.items.length; i++) {
if (
this.state.items[i].name
.toLowerCase()
.indexOf(this.state.value.toLowerCase()) !== -1
) {
searchResult.push(this.state.items[i]);
} else {
console.log('No matches on your search, try again');
}
}
e.preventDefault();
// If we have something in the object searchResult
if (searchResult.length > 0) {
this.setState({
error: false,
value: '',
searchResult: searchResult,
});
} else {
this.setState({
error: true,
value: '',
searchResult: [],
});
}
}
// call to the API
ponentDidMount() {
this.setState({ isLoading: !this.state.isLoading });
fetch('https://api.tvmaze./shows')
.then(response => response.json())
.then(data => {
this.setState({
items: data,
error: false,
});
this.setState({ isLoading: !this.state.isLoading });
})
.catch(console.error);
}
render() {
return (
<div className="App">
<Header />
<Loader isLoading={this.state.isLoading} />
<Input
handleChange={this.handleChange}
handleSubmit={this.handleSubmit}
value={this.state.value}
/>
{this.state.error ? (
<p className="errorMsg">No match on the search, try again!</p>
) : null}
<Search search={this.state.searchResult} />
</div>
);
}
}
export default App;
Input.js
function Input(props) {
return (
<div>
<form onSubmit={props.handleSubmit}>
<input
type="text"
className="input-style"
placeholder="Sök efter film.."
value={props.value}
onChange={props.handleChange}
/>
<button id="bold" className="button-style" type="submit">
<i className="fa fa-search" />
</button>
</form>
</div>
);
}
export default Input;
Share
Improve this question
edited Jun 20, 2018 at 7:29
Joshua
3,1963 gold badges26 silver badges41 bronze badges
asked Jun 20, 2018 at 7:13
LaconLacon
1051 gold badge3 silver badges12 bronze badges
1
- 3 You can change the className conditionally. stackoverflow./questions/30533171/… – Abin Thaha Commented Jun 20, 2018 at 7:16
3 Answers
Reset to default 3You can pass the error into the Input
ponent from the App
<Input
handleChange={this.handleChange}
handleSubmit={this.handleSubmit}
value={this.state.value}
error={this.state.error)
/>
and in your Input
ponent:
<input
type="text"
className={props.error ? 'error-input-style' : 'input-style'}
placeholder="Sök efter film.."
value={props.value}
onChange={props.handleChange}
/>
alternative you can also set an inline styling for the error condition:
<input
type="text"
className="input-style"
placeholder="Sök efter film.."
value={props.value}
onChange={props.handleChange}
style={{ border: props.error ? '1px solid red' : '' }}
/>
You can easily do this. Have a flag, say resultFound
, in the state of App.js
, with an initial value of false
. Then, in the function where you make the API call, update this resultFound
depending on whether any result was obtained.
And, in the render()
, before returning, assign inputClassName
dynamically based on the this.state.resultFound
, like so,
let inputClassName = '';
if (this.state.resultFound === false) {
inputClassName = 'input-style-error'; // new CSS class for errors
} else {
inputClassName = 'input-style';
}
Then, you can pass the inputClassName
as a prop to Input
and use it as <input>
's className, like so,
// in your App.js render() method's return
// ... existing ponents
<Input customStyle={inputClassName} ... />
// ...
<!-- in your Input.js render() method -->
<input type="text" className={props.customStyle} ... />
Whenever the API call happens, your state will change causing a re-render of the DOM (render()
is called). During each call, we dynamically set the inputClassName
based on the state's resultFound
. And, accordingly, the right className will be applied to the <input>
.
I will give bad names for classes and variables, just to make it super clear. You should use more generic ones.
The trick here is to give your Input a dynamic class via props, and if that expression turns true and the class is appended to the element, you can style it with css.
__CSS__
.input-style {
padding: 7px;
border-radius: 5px;
border: 1px solid #cccccc;
font-family: Courier New, Courier, monospace;
transition: background-color 0.3s ease-in-out;
outline: none;
}
.input-style:focus {
border: 1px solid turquoise;
}
.input-style.red-border {
border: 1px solid red;
}
__APP.js__
class App extends Component {
constructor() {
super();
this.state = {
value: '',
items: [],
isLoading: false,
searchResult: null,
error: false,
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
// To handle search
handleChange(e) {
this.setState({ value: e.target.value });
}
handleSubmit(e) {
let searchResult = [];
for (var i = 0; i < this.state.items.length; i++) {
if (
this.state.items[i].name
.toLowerCase()
.indexOf(this.state.value.toLowerCase()) !== -1
) {
searchResult.push(this.state.items[i]);
} else {
console.log('No matches on your search, try again');
}
}
e.preventDefault();
// If we have something in the object searchResult
if (searchResult.length > 0) {
this.setState({
error: false,
value: '',
searchResult: searchResult,
});
} else {
this.setState({
error: true,
value: '',
searchResult: [],
});
}
}
// call to the API
ponentDidMount() {
this.setState({ isLoading: !this.state.isLoading });
fetch('https://api.tvmaze./shows')
.then(response => response.json())
.then(data => {
this.setState({
items: data,
error: false,
});
this.setState({ isLoading: !this.state.isLoading });
})
.catch(console.error);
}
render() {
return (
<div className="App">
<Header />
<Loader isLoading={this.state.isLoading} />
<Input
handleChange={this.handleChange}
handleSubmit={this.handleSubmit}
value={this.state.value}
showRedBorder={this.state.error === true} // or what ever your logic
/>
{this.state.error ? (
<p className="errorMsg">No match on the search, try again!</p>
) : null}
<Search search={this.state.searchResult} />
</div>
);
}
}
export default App;
__Input.js__
function Input(props) {
return (
<div>
<form onSubmit={props.handleSubmit}>
<input
type="text"
className={`input-style${props.showRedBorder ? ' red-border' : ''}`}
placeholder="Sök efter film.."
value={props.value}
onChange={props.handleChange}
/>
<button id="bold" className="button-style" type="submit">
<i className="fa fa-search" />
</button>
</form>
</div>
);
}
export default Input;