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

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

Related

Is there an Angular way of doing: document.getElementById(id).style.display = "none";

I have a div with the id of 1. I'm trying to set the display to none dynamically. Is there an Angular way of doing this. Currently, I'm using vanilla javascript. I was asking about doing this dynamically because there will be over 60 divs that will be created from an array.
In my html
<div *ngFor="let item of items; i = index;">
<div id={{i}} (click)=hideDiv()></div>
</div>
In my method
hideDiv() {
return document.getElementById('1').style.display = "none";
}
That works but I'm looking for the Angular way of doing the above.
It was suggested that I use #ViewChild. Here's what I've changed. I can't use a Template Reference Variable as the html divs are created dynamically. Unless someone can let me know how to create the template variables dynamically. Although I don't think it's possible to create template variables with a loop.
#ViewChild('imgId', { static: true }) elementRef: ElementRef<HTMLDivElement>;
imgId: string;
Then in the method I have:
this.imgId = event.path[0].attributes[1].value;
this.elementRef.nativeElement.style.display = "none";
The event.path[0].attributes[1].value gets me the id of the image. The imgId shows when I console log it. It's still not changing the display on the div to none. Also I'm getting the error:
Cannot read properties of undefined (reading 'nativeElement')
Yes, you can use the ViewChild query in Angular to do this. In your component, define a query like this:
#ViewChild('#1') elementRef: ElementRef<HTMLDivElement>;
Implement the AfterViewInit interface in your component, and inside it, use this:
this.elementRef.nativeElement.style.display = "none";
You can simply use ngIf for this
Component
shouldDisplay: boolean = true;
hide(): void {
this.shouldDisplay = false;
}
show(): void {
this.shouldDisplay = true;
}
Html
<button (click)="hide()">Hide</button>
<button (click)="show()">Show</button>
<div *ngIf="shouldDisplay">this is the content</div>
Here is the working example
This is the Angular way:
template
<div *ngIf="showMe"></div>
or
<div [hidden]="!showMe"></div>
TypeScript:
showMe: boolean;
hideDiv() {
this.showMe = false;
}
For dynamic items where your don't know how many you will get the best approach would be to add a directive that would store and adjust that for you:
#Directive({ selector: '[hide-me]' })
export class HideDirective {
#Input() id!: string;
#HostBinding('style.display')
shouldShow: string = '';
}
then in your component just address them by ID:
#Component({
selector: 'my-app',
styleUrls: ['./app.component.css'],
template: `
<div *ngFor="let item of items; let index = index;">
<div hide-me id="{{index}}" (click)="hideDiv(index)">Some value</div>
</div>
`,
})
export class AppComponent {
#ViewChildren(HideDirective) hideDirectives!: QueryList<HideDirective>;
items = [null, null, null];
hideDiv(id: number) {
this.hideDirectives.find((p) => p.id === id.toString()).shouldShow = 'none';
}
}
Stackblitz: https://stackblitz.com/edit/angular-ivy-pnrdhv?file=src/app/app.component.ts
An angular official example: https://stackblitz.com/edit/angular-ivy-pnrdhv?file=src/app/app.component.ts
How about passing the div reference to the hideDiv method directly in the Dom using a template variable like this.
<div *ngFor="let item of items; i = index;">
<div #divElement (click)=hideDiv(divElement)></div>
And in your hide div method you will have access to the element directly
hideDiv(div) { div.style.display = "none";}
Here is a Stackblitz example
https://stackblitz.com/edit/angular-ivy-w1s3jl
There are many ways to do this, but in my opinion this is a simple solution the achieves your goal with less code.
PS:
It is always recommended to use the angular Renderer2 to manipulate Dom elements. This service has the method setStyle which you can use for your code.

OnClick check, remove & add class name in angular13

I want to add the class name 'active' onClick to 'li a' & also remove any 'active' class present on the 'li a'. The current code is working properly if I click sequence from top elements, but when I click elements from bottom to top, it's not working.
component.html
<div class="container text-center">
<ul id="myList" class="pt-5">
<li class="p-3">
List 1
</li>
<li class="p-3">
List 2
</li>
</ul>
</div>
component.ts
linkActive(event) {
const activeClass = event.srcElement.classList.contains('active');
const classFound = document.querySelector('li a');
const hpn = classFound.classList.contains('active');
if (activeClass == true) {
if (hpn == true) {
classFound.classList.remove('active');
}
alert('true');
event.srcElement.classList.remove('active');
} else {
if (hpn == true) {
classFound.classList.remove('active');
}
alert('false');
event.srcElement.classList.add('active');
}
}
Please find the sample code : https://stackblitz.com/edit/angular-ivy-jf9xvp
Do not manipulate the DOM directly like this,
You can use ngClass to achieve the desired result:
template:
List1
.ts
public activeList: number;
...
public linkActive(listNumber: number) {
this.activeList = listNumber;
}
In general, as recommended in comments, do the heroes tutorial and try to understand how to use typescript.

How to access dynamically created buttons in angular?

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.

share data between sibling components while both of them use *ngIf

So I have a parent component, that hosts 2 sibling components.
something like this
<div *ngif="somecode()">
<sibling1>
</sibling1>
</div>
<div *ngif="somecode()">
<sibling1 [dataParams]=sibling1object.somedata>
</sibling1>
</div>
so I get the error that sibling1object.somedata is undefined but when I remove ngIf() from 1st div, the error disappears.
It doesn't matter if *ngIf resolves to true or false. so i get the error even when sibling1 successfully loads.
Use [hidden]="!somecode()" instead of *ngIf="somecode()"
Please add following code in parent.component.ts
public siblingOneLoaded: boolean = false;
ngOnDestroy(): void {
if (sibling1object.somedata) {
this.siblingOneLoaded = true;
}
}
Add following code to html file
<div *ngIf="siblingOneLoaded">
<sibling1 [dataParams]=sibling1object.somedata>
</sibling1>
</div>

Angular2 - why doesn't async variable work with [hidden] directive?

I have links that I need to hide or show based on the services to or permissions of the current user. I have these services accessible via an Observable object and was hoping to use it in my template to determine what to hide/show.
I noticed though that this works for *ngIf but not for the [hidden] directive. I can't find any info on why this is?
Example:
#Component({
selector: 'my-app',
template: `
<a href="" *ngIf="(services$ | async)?.SPECIAL_FEATURE === true">
Save dropdown</a>
<a href="" [hidden]="(services$ | async)?.SPECIAL_FEATURE === false">
[Hidden] search</a>
<a href="" [hidden]="hideOtherLink">
The other link</a>
`
})
export class AppComponent implements OnInit {
name = 'Angular 5';
hideOtherLink = false;
services$: Observable<object>;
ngOnInit() {
this.services$ = Observable.timer(2000)
.map(() => {
return {
SPECIAL_FEATURE: true,
SPECIAL_THING: true
}
});
setTimeout(() => {
this.hideOtherLink = true;
}, 2000);
}
}
You got confused in your conditions there.
This one means
*ngIf="(services$ | async)?.SPECIAL_FEATURE === false"
If the condition is true, then show it
While this one means
[hidden]="(services$ | async)?.SPECIAL_FEATURE === false"
If the condition is true, then hide it.
They are the opposit of each other, that's why it is "not working".
EDIT You edited your question to set the ngIf to true, So I tested it on stack and I think I got your issue.
In your hidden condition, you test if the value of SPECIAL_FEATURE is equal to false.
This means
hide the content only if the special feature variable is equal to false.
But this doesn't cover the undefined or null values, for instance, which are falsy values.
Try this :
[hidden]="!(services$ | async)?.SPECIAL_FEATURE"

Categories

Resources