Add a class to a specific dom outside of Component in Angular2 - javascript

I have a component that opens up a modal based on if the a variable called 'isVisible' is True or False. Once the modal is visible, I would like to add a class to the 'body' tag of the page and once it is closed, I'd like to remove the class from the 'body' tag.
Below is a snippet of my code and what I have tried.
import {Component, ElementRef} from '#angular/core';
#Component({
selector: 'modal',
template: '<div class="modal_div">
<div (click)="closeCard()"></div>
<div class="modal_body">blah</div>
</div>'
})
export class DailogComponent{
isVisible: boolean;
constructor(public element: ElementRef){
this.isVisible = false;
}
OpenModal(){
this.isVisible = true;
//add css class to body here which is where I am lost
console.log(this.element.nativeElement.parentNode.querySelector('body'));
}
closeModal(){
this.isVisible = false;
//remove css class to body here which is where I am lost
console.log(this.element.nativeElement.parentNode.querySelector('body'));
}
}

Someone correct me if this is wrong or an anti pattern in ng.
You can just use javascript for that. If I understood correctly you want to change the class of the body tag. So the body of the page. <body></body>
How do I toggle an element's class in pure JavaScript?
and getElementsByTagName() or yeah query selector as you did, I personally use getElementsByTagName for no good reason other than I'm used to it.
//don't capitalize function names.
toggleBodyClass(){
this.toggleClass(document.getElementsByTagName('body')[0], 'myclass');
}
//this function is taken from the Stackoverflow thread posted above.
toggleClass(ele, class1) {
let classes = ele.className;
let regex = new RegExp('\\b' + class1 + '\\b');
let hasOne = classes.match(regex);
class1 = class1.replace(/\s+/g, '');
if (hasOne)
ele.className = classes.replace(regex, '');
else
ele.className = classes + class1;
}
tested, works

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.

Clone/Copy a Method in Javascript?

How does one clone or copy a method such that no reference to the original method is maintained?
I am trying to copy a click function on an element using the DOM and assign it to another element so I can remove the first element from the DOM without losing the function reference on the other element. I am doing this in a directive.
element.click = parent.click //need a better way to copy
element.click() //works
remove(parent)
element.click() //doesn't work
The reason why I am doing this is because I am removing the parent-wrapper tag, which has a (click) method assigned to it, so that just its inner button template remains. However, because I am removing the wrapper tag, the (click) on the parent tag is not being passed to the template button tag.
For instance, I have an app-button component with a button in its template.
Currently this is rendered:
<app-button (click) = function(1, 2)>
<button>
</button>
</app-button>
I want the parent tag removed, which I am doing via a DOM manipulation, but want to maintain the (click) function, like:
<button (click) = function(1, 2)>
</button>
I'm not quite sure I understand why you are trying to do what you are doing(or what it is exactly), but from my understanding, you could store a reference to the host element '' in the component class, then you can assign a listener to the <button> element in your template that triggers a click event on that reference:
import { BrowserModule } from '#angular/platform-browser';
import { NgModule, Component, ViewChild, ElementRef, Renderer2, OnInit} from '#angular/core';
#Component({
selector: 'app-root',
template: `
<app-button (click)="foo()"></app-button>
`
})
export class AppComponent {
foo() {
console.log('bar');
}// end function
}// end class
#Component({
selector: 'app-button',
template: `<button #button (click)="onClick()">Click me</button>`
})
export class AppButtonComponent implements OnInit {
#ViewChild('button')
private button:ElementRef;
constructor(
private element:ElementRef,
private renderer:Renderer2
) {}
ngOnInit() {
let parent = this.element.nativeElement.parentElement,
element = this.element.nativeElement,
button = this.button.nativeElement;
this.renderer.insertBefore(parent, button, element );
this.renderer.removeChild(parent, element);
}// end function
onClick() {
let element = this.element.nativeElement;
element.click();
}// end function
}// end class

Remove all occurances of class in angular 2 +

I have just started coding in angular5 and I came across need of removing all class occurances on click event.
Something like below we have in Jquery
$('.m-active').removeClass('m-active');
I am looking for alternative of this in angular2 + (Typescript)
You could use document.querySelector all to remove the class - in the following - I have two divs - iniitally set to be red / green text, but using querySelectorAll - I am removing the red class from the divs.
function toggleRedClass() {
var redDivs = document.querySelectorAll('.red');
if (redDivs.length) {
for(i=0;i<redDivs.length;i++) {
redDivs[i].classList.remove('red');
redDivs[i].classList.add('black')
}
} else {
var blackDivs = document.querySelectorAll('.black');
for(i=0;i<blackDivs.length;i++) {
blackDivs[i].classList.remove('black')
blackDivs[i].classList.add('red')
}
}
}
.red {color:red}
.green {color:green}
<div class="red">test</div>
<div class="green">test1</div>
<button type="button" onclick="toggleRedClass()">Click to toggle the red class</button>
In Angular 2+ better use bindings instead of jQuery
<div [class.my-class]="isMyClass">div 1</div>
<div [class.my-class]="isMyClass">div 2</div>
<button (click)="isMyClass = !isMyClass">toggle</button>
export class MyComponent {
isMyClass:boolean = true;
}
You can create a directive like this :
https://plnkr.co/edit/eKokX0IrsIWIuY9ACUZ4?p=preview
#Directive({
selector: '[class]'
})
export class ClassDirective {
#Input('class') claz;
private _claz;
public set claz(claz){
this._claz = claz;
}
public get claz(){
return this._claz;
}
#HostBinding('class') get hostClass(){
return this.claz;
}
constructor(){
console.log('***');
}
ngOnInit(){
console.log('this.classz',this.claz);
setTimeout(()=>{
this.claz= this.claz.replace('milad','');
},2000)
}
}
I know it doesn't do exactly what you want, but the idea is to create a Directive which has a selector called class and then you have access to all the classes in your application (obviously this component should be declared in your modules).
Then you can do whatever you'd like inside that directive, you can use host binding to override the classes and whatnot.
You can create an event listener to some button, pass the event listener's call back to this directive and let it do whatever you want.

#HostBinding disabling class from child component Angular 4

I have an angular app which has user login and logout. I am showing up a welcome page as the home page before a user logs in. I want to enable a background image only on the welcome page. Once the user logs in, the background image must disappear. When the user logs out, he will be redirected to welcome page which must show up the background image again.
I have tried using #HostBinding in the app.component.ts by renaming the selector to 'body'.
app.component.ts
import {Component, HostBinding, Input} from '#angular/core';
import {InputMask} from "primeng/primeng";
#Component({
selector: 'body',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
path = '../assets/img/AvayaHome.jpg';
title = 'app';
toggleClass = true;
#HostBinding('class.bodyClass') isWelcomePage = this.toggleClass;
}
Here is my CSS.
app.component.css
.bodyClass {
background-image: url("../assets/img/AvayaHome.jpg");
}
Here is my index.html
<!doctype html>
<html lang="en">
<head>
<title> Something </title>
</head>
<body class="bodyClass">
<app-welcome-page></app-welcome-page>
</body>
</html>
I am enabling the css style for bodyClass by assigning toggleClass as true. Once the user logs in, I am changing the value of toggleClass (which is in the app.component.ts) from the child component.
Here is my login.component.ts
onLogin() {
console.log('onLogin() invoked:', this._email, ':' , this.password);
if (this._email == null || this.password == null) {
this.errorMessage = 'All fields are required';
return;
}
this.errorMessage = null;
this.loginservice.authenticate(this._email, this.password);
this.appComponent.toggleClass = true;
this.router.navigate(['/dashboard']);
}
The value of the toggleClass changes when the user logs in to FALSE. But I am still seeing the background image. Not sure what I am doing wrong. Any help will be appreciated.
As an example, let's take a look at this code:
var toggleClass = false;
var isWelcomePage = toggleClass;
console.log(isWelcomePage); // prints true
Cool, all works as expected.
Ten seconds later....
Some user logins:
toggleClass = true;
console.log(isWelcomePage); // prints false
Why it has not changed???
If you open any documentation or any book about javascript you can read one main rule:
Primitives are always immutable.
When we assign toggleClass variable to isWelcomePage variable using =, we copy the value to the new variable because primitives are copied by value.
Solution 1:
Change isWelcomePage property directly
onLogin() {
...
this.appComponent.isWelcomePage = true;
...
}
Solution 2
Define getter
#HostBinding('class.bodyClass')
get isWelcomePage() {
return this.toggleClass;
}
Make a function with if and else;
if (user is login) {
document.body.classList.add('bodyClass');
} else {
document.body.classList.remove('bodyClass');
}
Than call that function when ever you need, logIn logOut etc
If you want to dynamically display and hide a background you should use a conditional class with ngClass
You can read more about it here NgClass
In your case it would be
<div [ngClass]="{'bodyClass': isWelcomePage}">
...
</div>
Then bodyClass css class will only apply IF isWelcomePage is true, if it's false it won't apply and the image won't show.
Edit:
As requested, a working example: Plunkr
Hostbinding only binds stuff to host tag, in your case tag.
So if you want to manipulate the body tag, you have to do it using plan javascript from your component or also create a component in the body.
#Component({
selector: 'body',
template: `<child></child>`
})
export class AppComponent {
#HostBinding('class') public cssClass = 'class1';
}

angular 2 bind to component selector

I have a component that I needs to be hidden when a property is true. Is there a way to solve this within the component itself.
Example:
#Component({
selector: 'prio-tab',
changeDetection: ChangeDetectionStrategy.OnPush,
template:
`
<div [hidden]="!active">
stuff
</div>
`
})
export class PrioTabComponent {
#Input() title;
active:boolean = false;
}
Here I would like to have the actual "prio-tab" element to depend on active-flag. Not just the content inside prio-tab.
Or is it maybe possible to use itself when declaring the prio-tab tag, like this:
<prio-tab [hidden]="this.active">
stuff
</prio-tab>
I guess a working solution would be to create a reference to the prio-tab component in its parent and then go through the parent. But how would I do if I have multiple prio-tab's ?
You can use #HostBinding()
export class PrioTabComponent {
#Input() title;
#HostBinding('hidden')
active:boolean = false;
}

Categories

Resources