How to access dynamically created buttons in angular? - javascript

So, I am creating a quiz application where I have a scrollbar for questions and in that scrollbar I have buttons depending on the length of a question set. So I've created those buttons using *ngFor directive. Now what I want to do is whenever a user selects any option (mcq), the question buttons in the scrollbar should get highlighted in following way:
If user selects any option, then change the question button color to Green
If user skips the question, then change the question button color to Yellow
HTML Code for Question Scrollbar:
<div id="answer-buttons" class="ques-grid ">
<button #navBarButton *ngFor="let item of items">{{item}}</button>
</div>
I'm have tried doing it by first accessing the buttons using ViewChild in ts file and then apply some logic, but it's not working, it is only changing the color of first button
#ViewChild('navBarButton',{ static: false }) navBarButton:ElementRef
//and in some function I've tried this logic
if(this.attemptedQuestionCount[this.currentQuestionIndex]==1){
this.navBarButton.nativeElement.style.backgroundColor = "#228B22"
}
else{
this.navBarButton.nativeElement.style.backgroundColor = "#FCF55F"
}
How can I achieve my objective?

You can check for attemptedQuestionCount and change background color like this
<div id="answer-buttons" class="ques-grid ">
<button *ngFor="let question of questions; let i=index"
[style.background-color]="attemptedQuestionCount[i] === 1 ? '#228B22' : '#FCF55F'">{{question}}</button>
</div>

Add button tag as follows:
<button *ngFor="let question of questions; let i=index"
[style.background-color]="attemptedQuestionCount[i] === 1 ? '#228B22' : '#FCF55F'">{{question}}</button>

You can add the click handler directly to the button using
<button *ngFor="let item of items; let indexOfelement=index"
(click)="heyYouClicked(indexOfelement)">{{item}}</button>
And then in the component you place the handler
export class AppComponent {
items = ["hello", "world"]
heyYouClicked(index){
console.log("you clicked " + index)
}
}

You can try ngClass for simplicity.
<button #navBarButton *ngFor="let item of items" class="defualt_state" [ngClass]="{'new_state': (condition_here)}">{{item}}</button>
And in the stylesheet you can have the above class configured
.new_state { background-color: #228B22 !important }
And set the default color of the button this way
.default_state { background-color : #FCF55F}
So when the condition matches it will take the color specified in the new_state class or else will take the default color from default_state class.

Related

Toggle class on buttons in Svelte

Consider this Svelte code
{#each filters as filters, i}
<div class='filter-container'>
<div class='button filter' on:click={ () => setFilter(filters.name, filterContext)}>{filters.name}</div>
</div>
{/each}
The above filter buttons are made depending on how many filters there are in some js object. How can I create a class that is toggled for all filter buttons when the one button is clicked.
eg. when one is active all the others are not?
Class are just strings - in svelte you can bind them to an expression as any other attribute.
For example:
<div class={'button filter ' + (activefilter === filters.name ? 'isactive' : '')}/>
when activefilter === filters.name is true, then the button class will became 'button filter isactive'
A special short syntax to toggle classes is provided too. Find more here
We can easily bind class names to expressions, so that the class will be added if the expression becomes truthy:
<script>
let isActive = false
</script>
<style>
.active {
color: red;
}
</style>
<h1 class:active={isActive}>Hello!</h1>
<button on:click={() => isActive = !isActive}>Click me</button>
Here, clicking the button toggles the isActive boolean. The class active is bound to that variable on the h1 element and you can see that the color now changes on every button click.
That is the standard way on how to set single classes dynamically.
REPL: https://svelte.dev/repl/988df145876a42c49bb8de51d2cae0f2?version=3.23.0

Angular 2 change ngFor items class knowing only it's index

I have a lot of buttons in my *ngFor, and I want that when someone click's on a button - it becomes active(it gets active class).
What I've done :
HTML :
<button
[ngClass]="{'activeBtn': buttActive }"
(click)="addDistrict(item);changeActive(i)"
*ngFor="let item of items; let i = index"
ion-button
#disable>
{{item.name}}
</button>
TS : (changing all buttons class to active (i want to change only that one i clicked)
buttActive = false;
changeActive(i) {
console.log(i);
this.buttActive = !this.buttActive;
}
have a buttActive property in the object and change it
button [ngClass]="{'activeBtn': item.buttActive }" (click)="addDistrict(item);changeActive(item,i)"
*ngFor="let item of items; let i = index" ion-button #disable>{{item.name}}</button>
changeActive(item, i){
console.log(i);
item.buttActive = !item.buttActive;
}
If you don't want to create a property on each item, then create a lastClickedIndex property in your Component class and set it with the index of the button that was clicked:
lastClickedIndex;
changeActive(i) {
this.lastClickedIndex = i;
}
And in your template, check for the lastClickedIndex button based on index to apply the activeBtn class.
<button
*ngFor="let item of items; let i = index"
[ngClass]="(lastClickedIndex == i) ? 'activeBtn': ''"
(click)="addDistrict(item);changeActive(i)"
ion-button
#disable>
{{item.name}}
</button>
That way you won't have to create a property on each item object. This will also take care of removing the class from the previously selected button when some other button is clicked.
Here's a StackBlitz for your ref.

Angular 6 [ngClass] not working with boolean in component.js

What I'm trying to do is hide text when ngState is true. When a certain element is clicked, that state is set to true. The [ngClass] should then add the hide class and hide the text. This first snippet is from the component.ts which outlines the boolean variable and the function which sets it to true.
export class MainMenuComponent implements OnInit {
ngState = false;
constructor() {
}
newGame(){
this.ngState = this.ngState === true ? false : true;
console.log(this.ngState);
}
}
This next snippet is the component html
<canvas id='sparkCanvas'></canvas>
<div class="menuBox">
<div class="title" [ngClass]="{'hide': ngState}">Dark Shards</div>
<div class="optContainer">
<ul>
<li *ngFor="let opt of opts" class="{{opt.class}}" [ngClass]="{'hide': ngState}" (click)="opt.f()">{{opt.n}}</li>
</ul>
</div>
</div>
and here is the hide class below
.hide{
opacity: 0;
}
When I replace [ngClass]="{'hide': ngState}" with [ngClass]="{'hide': true}"
It will then work as intended. What am I not understanding here?
Here is a link to my code with a working example:
https://stackblitz.com/edit/angular-fg48ro?file=src%2Findex.html
Try without Quote
<li *ngFor="let opt of opts" class="{{opt.class}}" [ngClass]="{hide: ngState}" (click)="opt.f()">{{opt.n}}</li>
EDIT
When i see your code, the issue is not related to angular, but with javascript context, you need to specifiy the context of this like
' f: this.newGame.bind(this),'
DEMO

How apply styles from Material Angular

I need to add a button with the Material Angular 2 style. So I have a button that does this:
<button mat-button (click)="addElement($event, 'button')">Add button</button>
Where addElement is defined:
addElement(ev, tag) {
const htmlElem = ev.currentTarget;
const el = this.renderer.createElement(tag);
this.renderer.setAttribute(el, 'mat-button', '');
el.textContent = 'New Button';
htmlElem.parentNode.insertBefore(el, null);
}
Clicking on the button then creates a new element in my HTML, shown as:
<button mat-button">Add button</button>
The function code correctly generates the button, however it doesn't apply all children as shown in the original button code from the material code
So do I have a way to "refresh" the button so that the everything from the standard Mat-button applies to the button added via the JS code?
What went wrong?
You are right that you have added the mat-button attribute but since you are creating a button dynamically you did not be able to achieve to generate the whole structure of mat-button.
Below is the whole style and structure of mat-button
<button class="mat-button" mat-button="">
<span class="mat-button-wrapper">Add button</span>
<div class="mat-button-ripple mat-ripple"></div>
<div class="mat-button-focus-overlay"></div>
</button>
As you can see there are other html elements in the mat-button which includes ripple effects and focus overlay.
The Solution to your question
We are going to use Angular Renderer to create html element, set the attribute to the element and to append it on an element.
So what we did
create the button that will trigger the append
<button id="clickBtn" (click)="onClick()">Click here to add Button</button>
import the import Directive, ElementRef, Renderer2 in the component.
{ Component, Directive, ElementRef, Renderer2 } from '#angular/core';
add a directive that will target the html element where the button will get appended (#clickBtn [is the id tag of the button that we created)
#Directive({
selector: '#clickBtn'
})
create a constructor to inject renderer and elementref
constructor(private renderer: Renderer2,private elRef: ElementRef) {
}
trigger the click event to append the button
onClick() {
const btn = this.renderer.createElement('button');
const span = this.renderer.createElement('span');
const div1 = this.renderer.createElement('div');
const div2 = this.renderer.createElement('div');
const text = this.renderer.createText('I am a Generated Button');
const attrBtn = this.renderer.setAttribute(btn, 'class', 'mat-button');
const attrSpan = this.renderer.setAttribute(span, 'class', 'mat-button-wrapper');
const attrDiv1 = this.renderer.setAttribute(div1, 'class', 'mat-button-ripple mat-ripple');
const attrDiv2 = this.renderer.setAttribute(div2, 'class', 'mat-button-focus-overlay');
this.renderer.appendChild(span, text);
this.renderer.appendChild(btn, span);
this.renderer.appendChild(btn, div1);
this.renderer.appendChild(btn, div2);
this.renderer.appendChild(this.elRef.nativeElement, btn);
}
Woah so what going on here. as you can see we generate here all the structure of the mat-button
To know more about Renderer2 Please visit this link.
https://alligator.io/angular/using-renderer2/
Please see the link of live code on stackblitz
https://stackblitz.com/edit/dmgrave-ng-so-answer-dom?file=app%2Fapp.component.ts
Hope this helps.
I'm not sure why you want to generate the buttons dynamically but I would do it something like this
In your template:
<button mat-raised-button (click)="addNewButton()">Add new </button>
<div *ngFor="let btn of buttonsList">
<button mat-raised-button color="primary">
{{btn+1}}
</button>
</div>
In your component:
export class AppComponent {
buttonsList = [];
addNewButton():void{
const newId = this.buttonsList.length;
this.buttonsList.push(newId);
}
}
You just have to loop through your array and display the buttons. Every time you add a new button, you just have to push a new value to your array and let the framework do the heavy lifting.
Here's the stackblitz demo : https://stackblitz.com/edit/angular-7rfwrx?file=app%2Fapp.component.ts
Hope this helps.

Getting HTML element by Id and switch its CSS through React

I have some files that load into my react components, which have HTML code.
As it is now, the pure HTML code renders just fine, however there is some 'hidden' code that appears whenever you click certain buttons in other parts of the application or on the text above (think of it like panels that expand when you click on it).
The HTML is hidden just using the good old <div id="someId" style="display:none">.
Anyway I am trying to get the correct panel to expand upon clicking their respective buttons.
So in theory, what I need to do is find the element by id, and switch it's display to block whenever needed, and then switch it back when the parent is clicked again.
Unfortunately I have no idea how to do this and so far have gotten nowhere. As it is now, I have access to the component's ids. What I want to know is how in the world can I access that and get to change whatever is rendering?
Create your function:
function element_do(my_element, what_to_do) {
document.getElementById(my_element).style.display = what_to_do;
}
and latter in code you can append wherever you want through javascript onclick or not depends what do you need:
element_do("someId", "none"); // to hide
element_do("someId", "block"); // to show
or create yourself toggle:
function toggle_element(element_id) {
var element = document.getElementById(element_id);
element.style.display = (element.style.display != 'none' ? 'none' : 'block' );
}
// and you can just call it
<button onClick="toggle_element('some_id')">toggle some element</button>
The react way to do it would be with states. Assuming that you know how to use states I'd do something like this:
class ShowHide extends React.Component {
constructor() {
super();
this.state = {myState: true};
this.onClick = this.onClick.bind(this)
}
onClick() {
this.setState({myState: !this.state.myState}) //set the opposite of true/false
}
render() {
const style = {myState ? "display: none" : "display:block"} //if myState is true/false it will set the style
return (<div>
<button onClick={this.onClick}>Click me to hide/show me </button>
<div id="myDiv" style={style}> Here you will hide/show div on click </div>
</div>)
}
}

Categories

Resources