Switching between buttons - javascript

How can i make dynamic switching between buttons? For example if one button is active, then the rest is not active. For example:
constructor(props) {
super(props)
this.state = {
active: false,
notactive: false
}
}
checkActive = () => {
this.setState({
active: true,
notactive: false
})
};
checkNotactive = () => {
this.setState({
active: false,
notactive: true
})
};
But i want make it dynamic.
I mean that when I have, for example, 10 buttons, I will not set each state apart. If I add another button, it will work. Just like the radio button.

Instead of a boolean you could use an index to mark the active button.
Eg:
this.state = {
activeButtonIndex: null
}
then when creating your buttons using a loop you can check
if (index === this.state.activeButtonIndex) { do something }

You would need to have a type property based on your buttons. If for example you need to make 3 buttons daily, weekly, monthly then you can have something like this.
return (
<div style={style.buttonContainer}>
<div className={this.props.active === "daily" ? "activeButton" : ""}>
<Button color="primary" onClick={this.handleClick("daily")}>
Daily
</Button>
</div>
<div className={this.props.active === "weekly" ? "activeButton" : ""}>
<Button color="primary" onClick={this.handleClick("weekly")}>
Weekly
</Button>
</div>
<div className={this.props.active === "monthly" ? "activeButton" : ""}>
<Button color="primary" onClick={this.handleClick("monthly")}>
Monthly
</Button>
</div>
</div>
);
I have a property on parent's state active and based on that I will switch dynamically.

Related

Expand/Collapse all data

I am making a Accordion and when we click each individual item then its opening or closing well.
Now I have implemented expand all or collapse all option to that to make all the accordions expand/collapse.
Accordion.js
const accordionArray = [
{ heading: "Heading 1", text: "Text for Heading 1" },
{ heading: "Heading 2", text: "Text for Heading 2" },
{ heading: "Heading 3", text: "Text for Heading 3" }
];
.
.
.
{accordionArray.map((item, index) => (
<div key={index}>
<Accordion>
<Heading>
<div className="heading-box">
<h1 className="heading">{item.heading}</h1>
</div>
</Heading>
<Text expandAll={expandAll}>
<p className="text">{item.text}</p>
</Text>
</Accordion>
</div>
))}
And text.js is a file where I am making the action to open any particular content of the accordion and the code as follows,
import React from "react";
class Text extends React.Component {
render() {
return (
<div style={{ ...this.props.style }}>
{this.props.expandAll ? (
<div className={`content open`}>
{this.props.render && this.props.render(this.props.text)}
</div>
) : (
<div className={`content ${this.props.text ? "open" : ""}`}>
{this.props.text ? this.props.children : ""}
{this.props.text
? this.props.render && this.props.render(this.props.text)
: ""}
</div>
)}
</div>
);
}
}
export default Text;
Here via this.props.expandAll I am getting the value whether the expandAll is true or false. If it is true then all accordion will get the class className={`content open`} so all will gets opened.
Problem:
The open class is applied but the inside text content is not rendered.
So this line doesn't work,
{this.props.render && this.props.render(this.props.text)}
Requirement:
If expand all/collapse all button is clicked then all the accordions should gets opened/closed respectively.
This should work irrespective of previously opened/closed accordion.. So if Expand all then it should open all the accordion or else needs to close all accordion even though it was opened/closed previously.
Links:
This is the link of the file https://codesandbox.io/s/react-accordion-forked-sm5fw?file=/src/GetAccordion.js where the props are actually gets passed down.
Edit:
If I use {this.props.children} then every accordion gets opened.. No issues.
But if I open any accordion manually on click over particular item then If i click expand all then its expanded(expected) but If I click back Collapse all option then not all the accordions are closed.. The ones which we opened previously are still in open state.. But expected behavior here is that everything should gets closed.
In your file text.js
at line number 9. please replace the previous code by:
{this.props.children}
Tried in the sandbox and worked for me.
///
cant add a comment so editing the answer itself.
Accordian.js contains your hook expandAll and the heading boolean is already happening GetAccordian.js.
I suggest moving the expand all to GetAccordian.js so that you can control both values.
in this case this.props.render is not a function and this.props.text is undefined, try replacing this line
<div className={`content open`}>
{this.props.render && this.props.render(this.props.text)}
</div>
by this:
<div className={`content open`}>
{this.props.children}
</div>
EDIT: //
Other solution is to pass the expandAll property to the Accordion component
<Accordion expandAll={expandAll}>
<Heading>
<div className="heading-box">
<h1 className="heading">{item.heading}</h1>
</div>
</Heading>
<Text>
<p className="text">{item.text}</p>
</Text>
</Accordion>
then in getAccordion.js
onShow = (i) => {
this.setState({
active: this.props.expandAll ? -1: i,
reserve: this.props.expandAll ? -1: i
});
if (this.state.reserve === i) {
this.setState({
active: -1,
reserve: -1
});
}
};
render() {
const children = React.Children.map(this.props.children, (child, i) => {
return React.cloneElement(child, {
heading: this.props.expandAll || this.state.active === i,
text: this.props.expandAll || this.state.active + stage === i,
onShow: () => this.onShow(i)
});
});
return <div className="accordion">{children}</div>;
}
};
Building off of #lissettdm answer, it's not clear to me why getAccordion and accordion are two separate entities. You might have a very valid reason for the separation, but the fact that the two components' states are interdependent hints that they might be better implemented as one component.
Accordion now controls the state of it's children directly, as before, but without using getAccordion. Toggling expandAll now resets the states of the individual items as well.
const NormalAccordion = () => {
const accordionArray = [ //... your data ];
const [state, setState] = useState({
expandAll: false,
...accordionArray.map(item => false),
});
const handleExpandAll = () => {
setState((prevState) => ({
expandAll: !prevState.expandAll,
...accordionArray.map(item => !prevState.expandAll),
}));
};
const handleTextExpand = (id) => {
setState((prevState) => ({
...prevState,
[id]: !prevState[id]
}));
};
return (
<>
<div className="w-full text-right">
<button onClick={handleExpandAll}>
{state.expandAll ? `Collapse All` : `Expand All`}
</button>
</div>
<br />
{accordionArray.map((item, index) => (
<div key={index}>
<div className="accordion">
<Heading handleTextExpand={handleTextExpand} id={index}>
<div className="heading-box">
<h1 className="heading">{item.heading}</h1>
</div>
</Heading>
<Text shouldExpand={state[index]}>
<p className="text">{item.text}</p>
</Text>
</div>
</div>
))}
</>
);
};
Heading passes back the index so the parent component knows which item to turn off.
class Heading extends React.Component {
handleExpand = () => {
this.props.handleTextExpand(this.props.id);
};
render() {
return (
<div
style={ //... your styles}
onClick={this.handleExpand}
>
{this.props.children}
</div>
);
}
}
Text only cares about one prop to determine if it should display the expand content.
class Text extends React.Component {
render() {
return (
<div style={{ ...this.props.style }}>
<div
className={`content ${this.props.shouldExpand ? "open" : ""}`}
>
{this.props.shouldExpand ? this.props.children : ""}
</div>
</div>
);
}
}

show element in v-for list: VueJS [duplicate]

This question already has an answer here:
Vue.js - Add class to clicked button
(1 answer)
Closed 3 years ago.
I have a v-for which display all my items and I have a panel for each items (to modify and delete) but when I click on this button to display my panel, it appears on all of my items. How can I avoid that ? This is the same thing when I click on modify button, the input to modify my item appears on each element.
There is my code :
<div v-for="(comment, index) in comments" :list="index" :key="comment">
<div v-on:click="show = !show">
<div v-if="show">
<button #click="edit(comment), active = !active, inactive = !inactive">
Modify
</button>
<button #click="deleteComment(comment)">
Delete
</button>
</div>
</div>
<div>
<p :class="{ active: active }">
{{ comment.content }}
</p>
<input :class="{ inactive: inactive }" type="text" v-model="comment.content" #keyup.enter="doneEdit">
</div>
</div>
And the methods & data :
data() {
return {
show: false,
editing: null,
active: true,
inactive: true
}
},
methods: {
edit(comment) {
this.editing = comment
this.oldComment = comment.content
},
doneEdit() {
this.editing = null
this.active = true
this.inactive = true
}
}
You have the same show, editing, active, inactive state for all items. So if you change some data property for one item it changed for all.
There are a lot of ways to achieve what you want.
The easiest is to manage your data by index.
For example:
<div v-on:click="showIndex = index">
<div v-if="showIndex === index">
...
data () {
return {
showIndex: null
...
The main problem with this approach - you can show/edit only one item at the time.
If you need more complicated logic and whant to manage more then one item at the time I suggest to create a separate component for your items and each will have own state (show, editing etc.)
#NaN's approach works if you want to only have one open at a time. If you want to have the possibility of having multiple open at the same time you would need to keep track of each individual element. Right now you are only basing it on show. Which can only be true/false for all elements at the same time.
So this is what you need to do:
Change show from a boolean to an array
data() {
return {
show: [],
editing: null,
active: true,
inactive: true,
}
},
Then you can keep track of which element should have the panel or not:
<div v-on:click="toggleActive(index)">
And the method:
methods: {
toggleActive(index) {
if (this.show.includes(index)) {
this.show = this.show.filter(entry => entry !== index);
return;
}
this.show.push(index);
}
}
and finally your v-if becomes:
<div v-if="show.includes(index)">

Creating show and hide sections with buttons in reactjs

I have three buttons that when clicking show and individual div but this is done in reactjs
import React, { Component } from 'react';
export class ModeExtended extends Component {
constructor() {
super();
this.busButton = this.busButton.bind(this);
this.trainButton = this.trainButton.bind(this);
this.tramButton = this.tramButton.bind(this);
this.state = {
isHidden: false,
}
}
busButton(){
console.log('Bus Button Was Pressed');
this.setState((prevState) => {
return{
isHidden: !prevState.isHidden
};
});
}
trainButton(){
console.log('Train Button Was Pressed');
this.setState((prevState) => {
return{
isHidden: !prevState.isHidden
};
});
}
tramButton(){
console.log('Tram Button Was Pressed');
this.setState((prevState) => {
return{
isHidden: !prevState.isHidden
};
});
}
render() {
return (
<div>
<h5>Mode Extended</h5>
<button onClick={this.busButton}>Bus</button>
<button onClick={this.trainButton}>Train</button>
<button onClick={this.tramButton}>Tram</button>
{this.state.isHidden && (
<div>
<h6>You can show Bus Data Now....</h6>
</div>
)}
{this.state.isHidden && (
<div>
<h6>You can show Train Data Now....</h6>
</div>
)}
{this.state.isHidden && (
<div>
<h6>You can show Tram Data Now....</h6>
</div>
)}
</div>
)
}
}
export default ModeExtended
When I click any of the buttons it shows all bus, tram and train data - how do I get them to just show one thing at a time and making sure that the other states are closed. I am really missing something here and need a pointer or two or three…
How can I add an ID to make each button open separate from each other and when one is clicked how can I close the rest of the divs - or open state, I am so lost here. Please help me out.
Cheers as always!
Here is a REPL of my code:
You need to have 3 different isHidden properties to control your divs. You can do it like this:
this.state = {
isHiddenBus: false,
isHiddenTrain: false,
isHiddenTram: false,
}
and then in your render like this:
{this.state.isHiddenBus && (
<div>
<h6>You can show Bus Data Now....</h6>
</div>
)}
{this.state.isHiddenTrain && (
<div>
<h6>You can show Train Data Now....</h6>
</div>
)}
{this.state.isHiddenTram && (
<div>
<h6>You can show Tram Data Now....</h6>
</div>
)}
also your buttons have to change to state accordingly to this.
busButton(){
console.log('Bus Button Was Pressed');
this.setState((prevState) => {
return{
isHiddenBus: !prevState.isHiddenBus
isHiddenTram: false
isHiddenTrain: false
};
});
}
trainButton(){
console.log('Train Button Was Pressed');
this.setState((prevState) => {
return{
isHiddenTrain: !prevState.isHiddenTrain
isHiddenBus: false
isHiddenTram: false
};
});
}
tramButton(){
console.log('Tram Button Was Pressed');
this.setState((prevState) => {
return{
isHiddenTram: !prevState.isHiddenTram
isHiddenTrain: false
isHiddenBus: false
};
});
}
you can do somthing like this:
import React, { Component } from 'react';
export class ModeExtended extends Component {
constructor() {
super();
this.state = {
curDivIndex:0,//currently visible div index
// isHidden: false,
}
}
renderDiv=()=>{
switch(this.state.curDivIndex){
case 1:return <div> <h6>You can show Bus Data Now....</h6> </div>
case 2:return <div> <h6>You can show Train Data Now....</h6> </div>
case 3:return <div> <h6>You can show Tram Data Now....</h6> </div>
}
return null
}
setVisibleDiv=(index)=>{
this.setState({curDivIndex:index})
}
render() {
return (
<div>
<h5>Mode Extended</h5>
<button onClick={()=>{this.setVisibleDiv(1)} }>Bus</button>
<button onClick={()=>{this.setVisibleDiv(2)}}>Train</button>
<button onClick={()=>{this.setVisibleDiv(3)}}>Tram</button>
{this.renderDiv()}
</div>
)
}
}
export default ModeExtended
EDIT
you want to have three different buttons, on click of each certain div
needs to be visible.
you can achieve this by maintaining the index of currently visible div.
when user clicks any button you have to set the index of div to be visible
which in the above code is achieved by using setVisibleDiv(index) call.
and you can at rendering time use curDivIndex to decide visible div.
Or you can achieve this by declaring state properties for all case:
this.state = {
hiddenBus: false,
hiddenTrain: false,
hiddenTram: false,
}
providing a name attribute to your buttons like so:
<button name="hiddenBus" onClick={toggleDisplay}>Bus</button>
<button name="hiddenTrain" onClick={toggleDisplay}>Train</button>
<button name="hiddenBus" onClick={toggleDisplay}>Tram</button>
then by defining the toggleDisplay function to toggle their display:
toggleDisplay = (event) => {
event.preventDefault(); // default behavior of a clicked button is to send a form so let's prevent this
const { name } = event.target; // find the clicked button name value
this.setState((prevState => ({
[name]: !prevState[name],
}));
}
Setting[name] enables us to target the state prop via the nameattribute value and update it based on the previous state.
Try this
import React, { Component } from "react";
export default class Create extends Component {
constructor(props) {
super(props);
this.state = {
currentBtn: null
};
}
clickedButton = e => {
this.setState({ currentBtn: e.target.id });
};
showDivElem = () => {
const { currentBtn } = this.state;
switch (currentBtn) {
case "A":
return <div>A</div>;
break;
case "B":
return <div>B</div>;
break;
case "C":
return <div>C</div>;
break;
default:
return <div>ABC</div>;
break;
}
};
render() {
console.log(this.state.currentBtn);
return (
<div>
<button id="A" onClick={e => this.clickedButton(e)}>
A
</button>
<button id="B" onClick={e => this.clickedButton(e)}>
B
</button>
<button id="C" onClick={e => this.clickedButton(e)}>
C
</button>
{this.showDivElem()}
</div>
);
}
}

Recreate the radio button behavior in ReactJS

I have recreated the radio button behavior in Sandbox.
When one button is active, all the other ones should be inactive.
Please find the link here :
https://codesandbox.io/s/amazing-chaplygin-xf8el?fontsize=14
For 3 buttons I have created 3 functions. Is there a way to optimize my code with 1 single function ?
Thank you
Yes, you can do that by using an Array:
import React, { useState } from "react";
export default function ButtonToggle() {
const [happy, setHappy] = useState([false, false, false]);
function handle(idx) {
let set = [false, false, false];
set[idx] = !happy[idx];
setHappy(set);
}
return (
<div>
{happy[0] ? "😄" : "🤕"}
<button
onClick={() => {
handle(0);
}}
>
bouton1
</button>
{happy[1] ? "😄" : "🤕"}
<button
onClick={() => {
handle(1);
}}
>
bouton2
</button>
{happy[2] ? "😄" : "🤕"}
<button
onClick={() => {
handle(2);
}}
>
bouton3
</button>
</div>
);
}
CodeSandbox: https://codesandbox.io/s/jolly-haibt-73df1

Add active class on a map items react

After map my array, I want to add an active css class if the user clicked on 1 element.
My map prototype :
publicationsPages.map((mod, i) => (
<div onClick={e => this.addActiveClass(i, e)} className=
{`${activeClasses[i] ? "active" : "inactive"} ${classes.modcontainer}
modcontainer`}>
<p className={`${classes.menuitems} menuitems`}>
{t(`Publications.${mod.name}`).toUpperCase()}
</p>
</div>
))
I have tried with a method called addActiveClass and a state :
this.state={activeClasses: [false, false, false, false]}
addActiveClass(index) {
const result = [...this.state.activeClasses.slice(0, index), !this.state.activeClasses[index], this.state.activeClasses.slice(index + 1)];
this.setState({activeClasses: result});
}
But that does not work.
Thank you in advance for your help
On the click of the element, you keep in your state the index of the selected item. In the item you can check if the index is the same as the index in the state and add the active class.
publicationsPages.map((mod, i) => (
<div
onClick={e => this.setState({activeIndex: i)}}
className= {`${i == this.state.activeIndex ? "active" : "inactive"}
>
...
</div>
))
Your default (or initial) value for activeIndex should be -1, then in the begging, no item will me active (if you want this).

Categories

Resources