ReactJS how to select sibling and add or remove his className - javascript

I have two inputs, one textarea and three labels. When input or textarea is in foucs, I want to add some class for some animation on label, but I can not select that specific label. Is there a way to do this?
here is a code
<form onSubmit={this.handleSubmit}>
<div className="contact-form">
<div className="contact-form_group">
<label htmlFor="name" className={`contact-label name`}>
Ime
</label>
<input onChange={this.handleChange} onFocus={this.handleFocus} id="name" value={this.state.name} className="contact-form_input" type="text" name="name" />
</div>
<div className="contact-form_group">
<label htmlFor="email" className={`contact-label email`}>
Email
</label>
<input onChange={this.handleChange} onFocus={this.handleFocus} id="email" value={this.state.email} className="contact-form_input" type="email" name="email" />
</div>
<div className="contact-form_group">
<label htmlFor="message" className={`contact-label message`}>
Message
</label>
<textarea className="contact-form_textarea" id="message" cols="3" rows="8" onChange={this.handleChange} onFocus={this.handleFocus} value={this.state.msg} name="msg" />
</div>
<Button className="button" type="submit" children="Send" />
</div>

If you're not worried about browser compatibility you could use the cutting edge CSS selector :focus-within
.contact-form > div:focus-within label
color: red
BUT I wouldn't actually recommend that just yet. It's still part of the working draft and not widely supported.
In react, you could keep track of the active element's name and conditionally apply a class on render to the label. So onFocus/onBlur set focusedElement, eg
onFocus(e) {
this.setState({focusedElement: e.currentTarget.getAttribute('name')})
}
and then in render do something like
className={`contact-label name ${this.state.focusedElement === 'name' ? 'focused' : ''}`}
Here's a working example -
class TodoApp extends React.Component {
constructor(props) {
super(props)
this.state = {
focusedElement: null
};
}
onFocus(e) {
this.setState({
focusedElement: e.currentTarget.getAttribute('name')
});
}
onBlur(e) {
this.setState({
focusedElement: null
});
}
render() {
return ( <
div >
<
label className = {
this.state.focusedElement === 'name' ? 'focused' : ''
} > Label For Name < /label> <
input name = 'name'
type = 'text'
onFocus = {
this.onFocus.bind(this)
}
onBlur = {
this.onBlur.bind(this)
}
/>
<
label className = {
this.state.focusedElement === 'other' ? 'focused' : ''
} > Label For Other Value < /label> <
input name = 'other'
type = 'text'
onFocus = {
this.onFocus.bind(this)
}
onBlur = {
this.onBlur.bind(this)
}
/> < /
div >
);
}
}
ReactDOM.render( < TodoApp / > , document.querySelector("#app"))
.focused {
color: red;
}
<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"></div>

You don't need to add a class to let your css if the input/textarea it's on focus. CSS can handle this for you.
input:focus {
background-color: 'red'; // or do some animation here
}

React doesn't have a very efficient way of handling nested child components in a way similar to this. The best way to handle styling for specific nodes/children would be to use classes and then css to style appropriately.
In your case you would do something like this:
.contact-form_input:focus {
background-color: #eee;
}
If you wanted to add some styling on hover or some additional animation it would be done in a similar way (using CSS):
.contact-form_input:hover {
background-color: #aaa;
-webkit-animation: fadein 0.5s;
animation: fadein 0.5s;
}
It would not be appropriate to incorporate JQuery with ReactJS as other posters have suggested.

There is a React synthetic event for onFocus. You could update the state as the focus changes and change each inputs className.
JSFiddle
<div className="form__group">
<label htmlFor="input1" className={focus === "input1" ? "focused__input" : "input"}>Input 1</label>
<input name="input1" onChange={this._onChange.bind(this)} onFocus={this._onFocus.bind(this, "input1")}/>
</div>

You can define ref to that label and on focus you can get the value of that lable using
// Define ref to label
// Get label object on focus change event and update the class accordingly for label
var object = this.refs.labelName;

You can use jquery npm install --save jquery
and use jquery's addClass method.
import $ from 'jquery'
.....
<form onSubmit={this.handleSubmit}>
<div className='contact-form'>
<div className='contact-form_group'>
<label htmlFor='name' className={`contact-label name`} id='l1' >Ime</label>
<input onChange={this.handleChange}
onFocus={()=>{$(#l1).addClass('classNameYouWantToAdd')}} id='name'
value={this.state.name}
className='contact-form_input'
type='text' name='name'/>
</div>
<div className='contact-form_group'>
<label htmlFor='email' className={`contact-label email`} id='l2'>Email</label>
<input onChange={this.handleChange}
onFocus={()=>{$(#l2).addClass('classNameYouWantToAdd')}} id='name'
id='email' value={this.state.email} className='contact-form_input' type='email' name='email'/>
</div>
<div className='contact-form_group'>
<label htmlFor='message' className={`contact-label message`}>Message</label>
<textarea className='contact-form_textarea' id='message' cols='3' rows='8' onChange={this.handleChange} onFocus={this.handleFocus} value={this.state.msg} name='msg'></textarea>
</div>
<Button className='button' type='submit' children='Send' />
</div>
</form>

Related

Forms validation condition With react

I'm learning reactjs and I have a problem with the validation in forms
so I want to have a condition if the user don't enter his name a message 'empty field' appear
else alert hello 'his name' appears
My code
import React ,{Component} from 'react';
class Formular extends Component {
constructor(props) {
super(props);
this.state={
nom:'',
prenom:'',
email:'',
password:'',
error:false,
NameErr:''
}
}
handleValidation() {
if (this.state.nom =='')
{ this.state.error=true
this.state.NameErr='empty field'
//console.log( this.state.NameErr)
return(
this.state.NameErr
)
}
}
handleClick(event){
event.preventDefault()
let error = this.handleValidation()
if (!error)
{
alert('hello '+ this.state.nom)
}
}
render() {
/* console.log('nom',this.state.nom)
console.log('prenom',this.state.prenom) */
return (
<div >
<div className="form__group field">
<input type="input" className="form__field"
placeholder="Name" name="name" id='name'
required size='100' value={this.state.nom}
onChange={(event)=>this.setState({nom:event.target.value})} />
<label for="name" className="form__label">Name</label>
</div>
<div className='alert'> {this.state.NameErr} </div>
export default Formular
You must not change the state by force
like this
this.state.NameErr='empty field'
this.state.error=true
You need to use setState
like this example
this.setState({NameErr:'empty field',error:true})
then you can in render method
{ this.state.error && <div className='alert'> {this.state.NameErr}</div>}
Thus the element will only be displayed if there is an error
Mutation of state isn't correct, always mutate state using setState()
this.setState({NameErr:'empty field', error: true})
jsx
{this.state.error && <div className='alert'> {this.state.NameErr} </div>}
First, you should wrap your input in form tag instead simply div.
Next use event onSubmit on your form to trigger submission of the form.
Create un method to check if your value is not empty, then do what you want
render () {
return <form onSubmit={handleClick}>
<div className="form__group field">
<input
id='name' name="name" type="text"
className="form__field"
size='100' required
placeholder="Name" value={this.state.nom}
onChange={(event)=>this.setState({nom:event.target.value})}
/>
<label for="name" className="form__label">Name</label>
</div>;
<div className='alert'> {this.state.NameErr} </div>
</form>;
}
NT: type attribute of input should be "text" and not "input"
EDIT: Like said other guys, change also your changing state.

Need to create a card component as many times as I click a button in React

So, I want to create a card component many times I want on a click of a button but the problem is that it just creates the cardComponent one time. What should I do? Is there anyone that I could create these.
This is the code:
class GCARD extends Component {
constructor(props) {
super(props);
// This state changes so the card is generated
this.state = {
change: '',
}
this.handel = this.handel.bind(this);
}
handel = (element) => {
// This is the element which creates the card.
element = <CardComponent data="Give a little detail about the thing's you like!"
heading= "Create Your Own Card" image="./owl.jpg"/>
this.setState({
change: element
});
}
render() {
return (
<div>
<div className="form-div">
<div>
<p className="form-title">Create Your Own Card</p>
<hr/>
</div>
<div>
<label className="form-label">Main Heading </label>
<input className="form-input" type="text"/>
<br/><br/>
<label className="form-label">Some Info</label>
<input className="form-input1" type="text"/>
<br/><br/>
{/* Just focus on the button */}
<button onClick={this.handel} className="form-btn">CREATE</button>
</div>
</div>
<div>
{this.state.change}
</div>
</div>
);
}
}
Your current code only replaces the element. You want to use an array instead e.g. use it like this
this.setState({
change: this.state.change.concat(element)
});
The problem on your code is that you override every time the same component. I changed your code for you to fix this:
class GCARD extends Component {
constructor(props) {
super(props);
// This state changes so the card is generated
this.state = {
change: [],
}
this.handel = this.handel.bind(this);
}
handel = (element) => {
// This is the element which creates the card.
let components = this.state.change;
element = <CardComponent data="Give a little detail about the thing's you like!"
heading="Create Your Own Card" image="./owl.jpg" />
components.push(element);
this.setState({
change: components
});
}
render() {
return (
<div>
<div className="form-div">
<div>
<p className="form-title">Create Your Own Card</p>
<hr />
</div>
<div>
<label className="form-label">Main Heading </label>
<input className="form-input" type="text" />
<br /><br />
<label className="form-label">Some Info</label>
<input className="form-input1" type="text" />
<br /><br />
{/* Just focus on the button */}
<button onClick={this.handel} className="form-btn">CREATE</button>
</div>
</div>
<div>
{this.state.change.map(comp => (comp))}
</div>
</div>
);
}
}
There are many different ways you can approach this. I would recommend binding an onClick handler to your create button which would push an object into an array, which then, in turn, you could use to set the state. Then in your render, map over the state to return each card. Remeber you may need to use appropriate CSS for margins, inline-flex etc.
eg:
clickHander(){
let arr = [];
arr.push({
<card/>
})
this.setState({change: arr})
}
render(){
const card = this.state.change.map((card) => { return ( card )})
return (
<div>
{card}
</div>
)
}
Hope this helps!

Is it possible to control the uncontrollable input text? - React

I set the input texts values from state when the user select an item from SelectField dropdown menu of Material UI library but when it sets, the input texts become unchangeable and what I need is that when the
Here is the image;
(Before selecting an item from SelectField dropdown menu)
After selecting an item from dropdown;
All the filled input texts now become unchangeable, the empty ones are still changeable as they don't get the value from state.
Here are the codes;
<div className="form-group row">
<label className="col-md-2 control-label">Device ID</label>
<div className="col-md-10">
<input type="text" className="form-control" id="deviceId" value={this.state.deviceId} placeholder="Device ID" ref="_deviceId" />
</div>
</div>
My state;
state = {
deviceId: null,
};
And I set the input texts, when the user select an item from dropdown;
saveDeviceInternal(index, value) {
if (this.props.templatesList.length > 0){
let deviceId = value;
this.setState({deviceId});
}else{
let deviceId = this.refs._deviceId.value;
this.setState({deviceId});
}
}
Try to add this method to your class:
handleChange(e) {
let deviceId = e.currentTarget.id
this.setState({deviceId: e.target.value});
}
and add it to your input component:
<input type="text" className="form-control" onChange={this.handleChange.bind(this)} id="deviceId" value={this.state.deviceId} placeholder="Device ID" ref="_deviceId" />

Disable input conditionally (Vue.js)

I have an input:
<input
type="text"
id="name"
class="form-control"
name="name"
v-model="form.name"
:disabled="validated ? '' : disabled"
/>
and in my Vue.js component, I have:
..
..
ready() {
this.form.name = this.store.name;
this.form.validated = this.store.validated;
},
..
validated being a boolean, it can be either 0 or 1, but no matter what value is stored in the database, my input is always disabled.
I need the input to be disabled if false, otherwise it should be enabled and editable.
Update:
Doing this always enables the input (no matter I have 0 or 1 in the database):
<input
type="text"
id="name"
class="form-control"
name="name"
v-model="form.name"
:disabled="validated ? '' : disabled"
/>
Doing this always disabled the input (no matter I have 0 or 1 in the database):
<input
type="text"
id="name"
class="form-control"
name="name"
v-model="form.name"
:disabled="validated ? disabled : ''"
/>
To remove the disabled prop, you should set its value to false. This needs to be the boolean value for false, not the string 'false'.
So, if the value for validated is either a 1 or a 0, then conditionally set the disabled prop based off that value. E.g.:
<input type="text" :disabled="validated == 1">
Here is an example.
var app = new Vue({
el: '#app',
data: {
disabled: 0
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<button #click="disabled = (disabled + 1) % 2">Toggle Enable</button>
<input type="text" :disabled="disabled == 1">
<pre>{{ $data }}</pre>
</div>
you could have a computed property that returns a boolean dependent on whatever criteria you need.
<input type="text" :disabled=isDisabled>
then put your logic in a computed property...
computed: {
isDisabled() {
// evaluate whatever you need to determine disabled here...
return this.form.validated;
}
}
Not difficult, check this.
<button #click="disabled = !disabled">Toggle Enable</button>
<input type="text" id="name" class="form-control" name="name" v-model="form.name" :disabled="disabled">
jsfiddle
You can manipulate :disabled attribute in vue.js.
It will accept a boolean, if it's true, then the input gets disabled, otherwise it will be enabled...
Something like structured like below in your case for example:
<input type="text" id="name" class="form-control" name="name" v-model="form.name" :disabled="validated ? false : true">
Also read this below:
Conditionally Disabling Input Elements via JavaScript
Expression You can conditionally disable input elements inline
with a JavaScript expression. This compact approach provides a quick
way to apply simple conditional logic. For example, if you only needed
to check the length of the password, you may consider doing something
like this.
<h3>Change Your Password</h3>
<div class="form-group">
<label for="newPassword">Please choose a new password</label>
<input type="password" class="form-control" id="newPassword" placeholder="Password" v-model="newPassword">
</div>
<div class="form-group">
<label for="confirmPassword">Please confirm your new password</label>
<input type="password" class="form-control" id="confirmPassword" placeholder="Password" v-model="confirmPassword" v-bind:disabled="newPassword.length === 0 ? true : false">
</div>
Your disabled attribute requires a boolean value:
<input :disabled="validated" />
Notice how i've only checked if validated - This should work as 0 is falsey ...e.g
0 is considered to be false in JS (like undefined or null)
1 is in fact considered to be true
To be extra careful try:
<input :disabled="!!validated" />
This double negation turns the falsey or truthy value of 0 or 1 to false or true
don't believe me? go into your console and type !!0 or !!1 :-)
Also, to make sure your number 1 or 0 are definitely coming through as a Number and not the String '1' or '0' pre-pend the value you are checking with a + e.g <input :disabled="!!+validated"/> this turns a string of a number into a Number e.g
+1 = 1
+'1' = 1
Like David Morrow said above you could put your conditional logic into a method - this gives you more readable code - just return out of the method the condition you wish to check.
You may make a computed property and enable/disable any form type according to its value.
<template>
<button class="btn btn-default" :disabled="clickable">Click me</button>
</template>
<script>
export default{
computed: {
clickable() {
// if something
return true;
}
}
}
</script>
Try this
<div id="app">
<p>
<label for='terms'>
<input id='terms' type='checkbox' v-model='terms' /> Click me to enable
</label>
</p>
<input :disabled='isDisabled'></input>
</div>
vue js
new Vue({
el: '#app',
data: {
terms: false
},
computed: {
isDisabled: function(){
return !this.terms;
}
}
})
To toggle the input's disabled attribute was surprisingly complex. The issue for me was twofold:
(1) Remember: the input's "disabled" attribute is NOT a Boolean attribute.
The mere presence of the attribute means that the input is disabled.
However, the Vue.js creators have prepared this...
https://v2.vuejs.org/v2/guide/syntax.html#Attributes
(Thanks to #connexo for this... How to add disabled attribute in input text in vuejs?)
(2) In addition, there was a DOM timing re-rendering issue that I was having. The DOM was not updating when I tried to toggle back.
Upon certain situations, "the component will not re-render immediately. It will update in the next 'tick.'"
From Vue.js docs: https://v2.vuejs.org/v2/guide/reactivity.html
The solution was to use:
this.$nextTick(()=>{
this.disableInputBool = true
})
Fuller example workflow:
<div #click="allowInputOverwrite">
<input
type="text"
:disabled="disableInputBool">
</div>
<button #click="disallowInputOverwrite">
press me (do stuff in method, then disable input bool again)
</button>
<script>
export default {
data() {
return {
disableInputBool: true
}
},
methods: {
allowInputOverwrite(){
this.disableInputBool = false
},
disallowInputOverwrite(){
// accomplish other stuff here.
this.$nextTick(()=>{
this.disableInputBool = true
})
}
}
}
</script>
Can use this add condition.
<el-form-item :label="Amount ($)" style="width:100%" >
<template slot-scope="scoped">
<el-input-number v-model="listQuery.refAmount" :disabled="(rowData.status !== 1 ) === true" ></el-input-number>
</template>
</el-form-item>
If you use SFC and want a minimal example for this case, this would be how you can use it:
export default {
data() {
return {
disableInput: false
}
},
methods: {
toggleInput() {
this.disableInput = !this.disableInput
}
}
}
<template>
<div>
<input type="text" :disabled="disableInput">
<button #click="toggleInput">Toggle Input</button>
</div>
</template>
Clicking the button triggers the toggleInput function and simply switches the state of disableInput with this.disableInput = !this.disableInput.
This will also work
<input type="text" id="name" class="form-control" name="name" v-model="form.name" :disabled="!validated">
My Solution:
// App.vue Template:
<button
type="submit"
class="disabled:opacity-50 w-full px-3 py-4 text-white bg-indigo-500 rounded-md focus:bg-indigo-600 focus:outline-none"
:disabled="isButtonDisabled()"
#click="sendIdentity()"
>
<span v-if="MYVARIABLE > 0"> Add {{ MYVARIABLE }}</span>
<span v-else-if="MYVARIABLE == 0">Alternative text if you like</span>
<span v-else>Alternative text if you like</span>
</button>
Styles based on Tailwind
// App.vue Script:
(...)
methods: {
isButtonDisabled(){
return this.MYVARIABLE >= 0 ? undefined: 'disabled';
}
}
Manual:
vue v2
vue v3
If isButtonDisabled has the value of null, undefined, or false, the
disabled attribute will not even be included in the rendered
element.
Bear in mind that ES6 Sets/Maps don't appear to be reactive as far as i can tell, at time of writing.
We can disable inputs conditionally with Vue 3 by setting the disabled prop to the condition when we want to disable the input
For instance, we can write:
<template>
<input :disabled="disabled" />
<button #click="disabled = !disabled">toggle disable</button>
</template>
<script>
export default {
name: "App",
data() {
return {
disabled: false,
};
},
};
</script>
There is something newly released called inert, which is literally making it ignored by the browser.
<template>
<input
type="text"
id="name"
class="form-control"
name="name"
:inert="isItInert"
/>
</template>
<script setup>
const isItInert = true
</script>
Here is the playground for testing purposes.
Vue 3
<input
type="text"
id="name"
class="form-control"
name="name"
v-model="form.name"
:disabled="VALIDATOR == '0'"
/>

Reactjs how to manage multiple interactions between component

I struggling and a bit lost with react about one thing.
I have a row with 5 columns.
Each columns have a checkbox, an input and a button.
So if you look at it in a standard way, it looks like this:
render() {
return (
<div className="sbm-sources">
<div className="col-md-2 sbm-source">
<input type="checkbox" name="c1"/>Add to source.
<br />
<input type="text" name="i1" size="10" onClick={this.expandInput} placeholder="Enter you query." />
</div>
<div className="col-md-2 sbm-source">
<input type="checkbox" name="c2"/>Add source to blipp.
<br />
<input type="text" name="i2" size="10" onClick={this.expandInput} placeholder="Enter you query." />
</button>
</div>
<div className="col-md-2 sbm-source">
<input type="checkbox" name="c3" />Add source to blipp.
<br />
<input type="text" name="i3" size="10" onClick={this.expandInput} placeholder="Enter you query." />
</button>
</div>
<div className="col-md-2 sbm-source">
<input type="checkbox" name="c4" />Add source to blipp.
<br />
<input type="text" name="i4" size="10" onClick={this.expandInput} placeholder="Enter you query." />
</div>
<div className="col-md-2 sbm-source">
<input type="checkbox" name='c5' />Add source to blipp.
<br />
<input type="text" name="i5" size="10" onClick={this.expandInput} placeholder="Enter you query." />
</div>
</div>
);
}
};
The thing is, each column can be validated separately but I need to know which one is trigger.
I know how to do it using the name of each one, but I am not sure that creating a state for EACH input / checkbox and then check which one is triggered one for then associating the data before sending a POST request is the best option here.
ex:
handleChecked(e){
if (e.value.name === c1){
this.setState({checkedC1: true});
}
...
}
This would quickly become messy and hard to maintain, or to make adaptable.
The thing is, when I want to do my Post request, I would love to receive an int. For example, if the checkbox (and / or) input filled is from the first column, the int received would be 0.
Is there ean elegant way of doing so with react? What would you suggest?
I am too much into my code and my lack of experience make me blind about it.
Many thanks!
You would need to keep the state of all your columns inside the parent component, because from there you send your post request.
create an array of column data, and put the array in state
inside render, use .map() to loop over the array and render a Column for each item in the array
optional: put the column inside a separate (stateless) component.
Your state could be like:
// as part of your constructor
let initialColumns = [
{ checked: false, checkText: "Add to source.", inputPh="Enter your query.", value: ""},
...
{ checked: false, checkText: "Add source to blipp.", inputPh="Enter your query.", value: ""}
];
this.state = { colDataArr: initialColumns }
And in your render do:
render() {
let expandInput = this.expandInput;
<div>
{this.state.colDataArr.map( colItem, index => {
<Column
checked = {colItem.checked}
checkText = {colItem.checkText}
...
expandInput = {(e) => { expandInput(e) }} // <== special here
colID = {index} // and here
})}
</div>
}
Create a (stateless) <Column> component that takes the function expandInput as a prop, alongside the other variable props. Whenever this function is called, you get the event, but also the index of the column (from 0-4).
That way, inside expandInput, you can handle one individual update
expandInput(event, type, index) {
// create copy of column object, to avoid mutating state directly
let origColData = this.state.colDataArr[index]
let newColData = {
checked = origColData.checked,
checktext = origColData.checkText,
...
}
// now, do whatever you need with the event data to update the newColData
if (type == "checkbox") {
let checked = e.target.checked
} else {
...
}
// copy the array and replace the updated one
let newColArr = this.state.colDataArr.slice()
newColArr[index] = newColData
// set the new state
this.setState({ colDataArr : newColArr })
}
UPDATE
And your shiny new stateless component could look something like this:
class Column extends React.Component {
constructor(props) {
super(props)
}
render() {
<div className="col-md-2 sbm-source">
<input type="checkbox"
onClick={(e) => this.props.expandInput(e,"checkbox", this.props.colID)}/>
{this.props.checkText}
<br />
<input type="text" size="10"
onChange={(e) => this.props.expandInput(e,"text", this.props.colID)}
placeholder={this.props.inputPH} />
<button
onClick={(e) => this.props.expandInput(e,"button", this.props.colID)}>
Do something
</button>
</div>
}
}
Slightly easier way to maintain is to store the exact field variable name in the name instead, and do:
handleChecked(e){
this.setState({[e.value.name]: true});
...
}

Categories

Resources