How can I keep the selection active state of a button? - javascript

In this quiz selection component I put two different div bootstrap list-group with buttons.
I'd like to keep the active class when I click on some button but only one of them inside each div can be active.
So for example if I click on "History" and then "Medium", I'd like them to keep active class.
I've tried to add some bootstrap 4 options like "Active state" and tried to toggle active class onClick.
But in this way active class still stay in each button I click.
Thank you very much for help
class Selection extends React.Component {
render() {
return (
<div className="border rounded w-75 mx-auto">
<div className="row pt-4">
<div className="col-6">
<div
onClick={this.props.chooseCategory}
className="list-group w-75 ml-auto"
>
<p>Choose a category</p>
<button
type="button"
className="list-group-item list-group-item-action" active
>
Mix
</button>
<button
type="button"
className="list-group-item list-group-item-action"
>
Sports
</button>
<button
type="button"
className="list-group-item list-group-item-action"
>
History
</button>
<button
type="button"
className="list-group-item list-group-item-action"
>
Movies
</button>
<button
type="button"
className="list-group-item list-group-item-action"
>
Geography
</button>
</div>
</div>
<div className="col-6">
<div
className="list-group w-50 mr-auto"
onClick={this.props.chooseDifficulty}
>
<p>Choose the difficulty</p>
<button
type="button"
className="list-group-item list-group-item-action active"
>
Easy
</button>
<button
type="button"
className="list-group-item list-group-item-action"
>
Medium
</button>
<button
type="button"
className="list-group-item list-group-item-action"
>
Hard
</button>
</div>
</div>
</div>
<button
onClick={this.props.handleStart}
className="btn btn-outline-primary my-4"
>
START
</button>
</div>

Add state to the component, to represent the actively selected category and difficulty.
i.e. (in constructor)
this.state = { category: 'Mix', difficulty: 'Easy' }
Later in the render function you can check, using an if or inline ternary statement. I would also create an array representing the options and use array.map() to shorten your code.
const categoryOptions = [ 'Mix', 'Sports', 'History']
(in render function, instead of manually adding all those buttons...)
{ categoryOptions.map(category => (
<button
type="button"
className={`list-group-item list-group-item-action $(this.state.category === category ? 'active' : '')`}
onClick=(() => this.setState({category: category}))
>
{category}
</button>
)) }
The onClick will alter the state to represent the active element, the active class will conditionally be shown, only when that category has been selected.
I don't want to take this answer too far astray, but a further enhancement would be using the React useState hook with functional components, from the latest version of React.

use state to show the active div elements
onClick of the div make the state active as true
<div>
{this.state.active ? <div><button1><div> : <div><button2></div>}
</div>

Related

how to avoid changing of ng bootstrap dropdown when enter key is pressed on any on input field in angular13

i am using angular powered bootstrap dropdown and here whenever i do enter key press in input, it always changes the dropdown value to first option.
DEMO LINK
HTML:
<div class="row mb-3 mt-3">
<div class="col">
<div ngbDropdown class="d-inline-block">
<button
type="button"
class="btn btn-outline-primary"
id="dropdownBasic1"
ngbDropdownToggle
>
{{ clickMessage }}
</button>
<div ngbDropdownMenu aria-labelledby="dropdownBasic1">
<button
ngbDropdownItem
class="nav-link"
href="#"
(click)="getText($event.target.innerText)"
>
Option 1
</button>
<button
ngbDropdownItem
class="nav-link"
href="#"
(click)="getText($event.target.innerText)"
>
Option 2
</button>
<button
ngbDropdownItem
class="nav-link"
href="#"
(click)="getText($event.target.innerText)"
>
Option 3
</button>
</div>
</div>
</div>
</div>
TS:
clickMessage = 'Choose an option';
getText(text) {
this.clickMessage = text;
}
The behaviour you are seeing is due to you have some buttons without any type attribute and by default the buttons in the form trigger the submit event.Add the type to the buttons in the dropdown and it should work.
<div ngbDropdownMenu aria-labelledby="dropdownBasic1">
<button
type="button"
ngbDropdownItem
class="nav-link"
href="#"
(click)="getText($event, $event.target.innerText)"
>
Option 1
</button>
<button
type="button"
ngbDropdownItem
class="nav-link"
href="#"
(click)="getText($event, $event.target.innerText)"
>
Option 2
</button>
<button
ngbDropdownItem
class="nav-link"
href="#"
(click)="getText($event, $event.target.innerText)"
>
Option 3
</button>
</div>
Add the following code to your ts file.
#HostListener('keydown', ['$event']) public onKeyDown(evt) {
if (evt.key === 'Enter') {
evt.preventDefault();
this.onSubmit();
}
}
I write it for you in the following stackblitz.
https://stackblitz.com/edit/angular12-bootstrap-skiant?file=src%2Fapp%2Fapp.component.ts,src%2Fapp%2Fapp.component.html,src%2Fapp%2Fapp.module.ts

Bootstrap 4 collapse not working with map in ReactJS

I am trying to add collapse to a project where it will display all the available content when Read More button (which is working for collapsing button) got clicked. But, it only collapsing for the first item that fetched from the database, and for every other Read More button click it is not working for that particular item rather it keeps opening and closing the first item. Below is my code that I am trying with. I need help with this issue. Thanks very much.
<div>
<div className="container mt-3">
<h1 className="text-justify mb-4">The Blogstar Blog</h1>
<div>
{data && data.length > 0 ? (
data.map((item, index) => {
return (
<div className="card p-3 mt-3" key={index}>
<div className="d-flex">
<div className="text-justify mr-5">
<h3>{item.title}</h3>
</div>
<div className="d-flex ml-auto ">
<p className="mt-2 mr-2">
<img
src="https://img.icons8.com/material-rounded/18/000000/facebook-like.png"
alt="facebook-like-icon"
/>{' '}
{item.upvotes}
</p>
<p className="mt-2 mr-2">
<img
src="https://img.icons8.com/material-rounded/18/000000/thumbs-down.png"
alt="thumbs-down-icon"
/>{' '}
{item.downvotes}
</p>
<p className="mt-2">
<img
src="https://img.icons8.com/ios-filled/18/000000/comments.png"
alt="comments-icon"
/>{' '}
{item.comments}
</p>
</div>
</div>
<div className="d-flex">
<p>
<img
src="https://img.icons8.com/ios-glyphs/18/000000/clock.png"
alt="clock-icon"
/>
{formatDate(item.createdAt)}{' '}
</p>
<p>
<img
className="ml-2"
src="https://img.icons8.com/material-rounded/18/000000/writer-male.png"
alt="author-icon"
/>{' '}
<strong>{item.author.name}</strong>
</p>
</div>
<div className="text-justify">
{ReactHtmlParser(item.content.substring(0, 150))}
<button
class="btn btn-primary"
type="button"
data-toggle="collapse"
data-target="#collapseExample"
aria-expanded="false"
aria-controls="collapseExample">
Read More
</button>
<div class="collapse" id="collapseExample">
{ReactHtmlParser(item.content)}
</div>
</div>
</div>
);
})
) : (
<div>Nothing in database</div>
)}
</div>
</div>
</div>
Change the data-target on your collapse button to this:
data-target={`#collapseExample${index}`}
and your collapse div to this:
<div class="collapse" id={`collapseExample${index}`}>
// ... content
</div>
The problem was that your collapse id and button data-target were static. These values need to be dynamic depending on the data you're mapping through. Otherwise your button will always target the same element. This explains why every button collapses/expands the same div.

Disable one button in ngFor in Angular

I have an add to list button in my component, that disables when a user clicks it once. Problem is when the user clicks the button next to the list item they want to add, it disable all the other list buttons also? I only want it to disable the one selected. My code so far is:
component.html
<div *ngFor="let itunes of data | paginate: { itemsPerPage: 10, currentPage: p } | filter:filter; let i = index">
<div class="row">
<div class="col-lg-2 col-md-2 col-sm-12">
<img class="align-self-start mr-5 mb-5 artwork" src="{{itunes.artworkUrl100}}" alt="image">
</div>
<div class="col-md-8 col-lg-8 col-sm-12">
<h2 class="mt-0 d-inline-block text-truncate trunc">{{itunes.collectionName}}</h2>
<h4 class="mt-0 mb-3">{{itunes.artistName}}</h4>
<h5 class="mt-0 mb-3">Listen</h5>
</div>
<div class="col-md-2 mb-5 col-lg-2 col-sm-12">
<a target="_blank"><button type="button" class="btn btn-primary btn-lg float-right"
(click)="addSongToPlaylist(itunes); clicked = true;" [disabled]="clicked">
<fa-icon [icon]="faPlus">
</fa-icon>
</button></a>
</div>
</div>
</div>
component.ts (Add function)
addSongToPlaylist(itunes) {
this.list.playlist.push(Object.assign({}, itunes));
this.list.savePlaylist();
console.log('Playlist - ', this.list.playlist);
}
Any ideas, what I am doing wrong?
You have only one clicked property and all buttons have their disabled property bound to that property. That is why they are all enabled or disabled at the same time.
The simplest solution is to add a clicked property to the Itunes class, and modify its value when the button is clicked:
(click)="addSongToPlaylist(itunes); itunes.clicked = true;" [disabled]="itunes.clicked"
An alternative is to store the clicked value in a separate array, where each array item refers to an item of the data array.
Maybe do this:
<button type="button" class="btn btn-primary btn-lg float-right"
(click)="addSongToPlaylist(itunes); clicked = i;" [disabled]="clicked===i">
<fa-icon [icon]="faPlus">
</fa-icon>
</button>

ReactStrap modal close icon is not getting show in the modal and How to use the header with some another tag

I am new to the react js. Here I am using the Modal of reactStrap.
<Modal isOpen={props.isOpen} centered='true'>
<ModalHeader>
Change this Question?
<button type="button" class="close" aria-label="Close" onClick={props.closeModal} ><span aria-hidden="true">×</span></button>
</ModalHeader>
<ModalBody>
<div className="row">
<div className="col-md-12 text-center ">
<Button type="submit" className="btn btn-outline-primary" onClick={props.showChangeQuestionModal} >Change by creating your own Question</Button>
</div>
</div>
<div className="row">
<div className="col-md-12 text-center marginForOrOption">
<span>or</span>
</div>
</div>
<div className="row">
<div className="col-md-12 text-center">
<Button type="submit" color="btn btn-primary">Change and Replace from Question bank</Button>
</div>
</div>
</ModalBody>
<ModalFooter>
</ModalFooter>
</Modal>
</div>
Now Here I have added that button to close modal . But In reactstrap it comes by default.But In my case it is not coming .
Another problem is that,
In the ModalHeader, It comes by default h5 so How can I change that ?
First question: You need to provide toggle method to your ModalHeader component's props if you want reactstrap to show its own close button so your ModalHeader should looks like:
<ModalHeader toggle={this.toggleModalMethod}>
Change this Question?
</ModalHeader>
Second question: You are not gonna do much with h5 inside of modal header but you definitely can change your h5 element style to make it looks how you want it to look:
<ModalHeader>
<span className="customStyleToOverrideDefault">Change this Question?</span>
</ModalHeader>
Please, dont forget to vote up for my answer if it helped you.

Make a button inside an element clickable while the element itself is also clickable

I have a simple sidebar, that has a bunch of list items in it, and a button right next to the list item, like so:
I attached a click handler to the <li> element like in the code below:
<li className="note" onClick={()=> props.selectNote(props.note)} role="button">
<button className="delete-note" onClick={() => console.log('Fired')}>Delete dis</button>
<span className="updated-at">2hr</span>
<div className="note-content">
<h4 className="note-title">
{title}
</h4>
<p className="note-preview">
{notePreview.substr(0, 80)}...
</p>
</div>
</li>
But as expected, when I click the button next to it, the actual li gets clicked and not the button inside it. I think this is some sort of issue with how the event bubbles, and how it's a bad practice to attach onClick handlers to non-interactive elements (My ESLint says that).
What I want instead:
When the list item gets clicked, the attached onClick event fire.
When the button gets clicked, fire the onClick event on the button, and not the <li>.
Hack incoming!
I solved this by adding a name attribute to the elements that I didn't want to trigger the main click event:
handleClick = (e) => {
if(e.target.name === 'deleteButton') {
e.preventDefault();
e.stopPropagation();
}else {
this.props.selectNote(this.props.note)
}
}
<li className="note" onClick={this.handleClick} role="button">
<button className="delete-note" name="deleteButton" onClick={() => console.log('Fired')}>Delete dis</button>
<span className="updated-at">2hr</span>
<div className="note-content">
<h4 className="note-title">
{title}
</h4>
<p className="note-preview">
{notePreview.substr(0, 80)}...
</p>
</div>
</li>
You need to check which element triggered the event, and prevent it if it was the button.
What you need is to prevent further propagation of the current event (click) in the capturing and bubbling phases.
See the example on event propagation: https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Examples#Example_5:_Event_Propagation
For Button onClick the default click parameter event args is passed as 'event'. Use the event.stopPropagation() to stop propogate the click event back to li.
<li className="note" onClick={()=> props.selectNote(props.note)} role="button">
<button className="delete-note" onClick={(event) => event.stopPropagation(); console.log('Fired')}>Delete dis</button>
<span className="updated-at">2hr</span>
<div className="note-content">
<h4 className="note-title">
{title}
</h4>
<p className="note-preview">
{notePreview.substr(0, 80)}...
</p>
</div>
<li data-is-parent className="note" onClick={(e)=> e.target.getAttribute('data-is-parent') && props.selectNote(props.note)} role="button">
<button className="delete-note" onClick={() => console.log('Foreground Fired')}>Delete dis</button>
<span className="updated-at">2hr</span>
<div className="note-content">
<h4 className="note-title">
{title}
</h4>
<p className="note-preview">
{notePreview.substr(0, 80)}...
</p>
</div>
</li>
you should wrap your li in a div
like below
<div>
<li className="note" onClick={()=> props.selectNote(props.note)} role="button">
<span className="updated-at">2hr</span>
<div className="note-content">
<h4 className="note-title">
{title}
</h4>
<p className="note-preview">
{notePreview.substr(0, 80)}...
</p>
</div>
</li>
<button className="delete-note" onClick={() => console.log('Fired')}>Delete dis </button>
</div>

Categories

Resources