Attach event to all elements with a specific class - css

I would like to attach an event listner in my component that will fire for all elements that have a specific class.
These are generated by a third party component so i cannot attach it at the point where they are created.
i would like to do effectively do the equivalent of the following jquery statement but in angular where .day is the class.
$(document).on('mousedown','.day',function(jsEvent){
});
Thanks

You can use a Directive to attach the listener:
import { Directive, HostListener } from '#angular/core';
#Directive({
selector: '.day'
})
export class DayDirective {
#HostListener('mousedown', ['$event'])
yourFunction(event: Event): void { ... }
}
To wire up the Directive, you add it to the module that requires it. This could be your app module, a shared module or a feature module.
import { NgModule } from '#angular/core';
#NgModule({
declarations: [
DayDirective,
],
...
})
export class YourModule {}

Related

Imported angular module doesn't work the first time a route is visited, but it works in the subsequent visits

I have a simple Loader Module hosted in a private npm repository. This module only intercepts http calls and displays a css loader. The code is simple: it has a public api that exports the module and a component. The module has an http interceptor that calls a service which sets a variable isLoading.
The component code is split into three files, as usual (ts, html and scss).
loader.component.ts
import { Component, OnInit, AfterViewInit } from '#angular/core';
import { LoaderService } from '../service/loader.service';
#Component({
selector: 'loader',
templateUrl: './loader.component.html',
styleUrls: ['./loader.component.scss']
})
export class LoaderComponent implements OnInit {
public isLoading: boolean;
constructor(private loaderService: LoaderService) { }
ngOnInit(){
this.loaderService.isLoading.subscribe(status => this.isLoading = status);
}
}
loader.component.html
<div *ngIf="isLoading" class="overlay">
<div class="loader center-block"></div>
<p class="wait">
<strong>Wait... </strong>
</p>
</div>
The loader.component.scss file defines the loader, overlay and wait css classes (not shown for brevity).
The interceptor code is here:
#Injectable()
export class LoaderInterceptor implements HttpInterceptor {
constructor(private loaderService: LoaderService) {}
intercept(
request: HttpRequest<unknown>,
next: HttpHandler
): Observable<HttpEvent<any>> {
this.loaderService.show();
return next.handle(request).pipe(finalize(() => this.loaderService.hide()));
}
}
And finally, the service code:
import { Injectable } from '#angular/core';
import { Subject } from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class LoaderService {
public isLoading: Subject<boolean>;
constructor() {
this.isLoading = new Subject<boolean>();
}
show() {
this.isLoading.next(true);
}
hide() {
this.isLoading.next(false);
}
}
This module is imported at the app.module.ts of an application and declared in the imports array of the decorator.
The application has some routes with lazy load feature modules.
The loader module is used at the app.component.html file, as follows:
<loader></loader>
<router-outlet></router-outlet>
The problem: the loader does not work the first time we visit a route, but it works if we click the previous button at the browser or we click to go back to homepage or any other way I tested visiting a route twice or more. Otherwise, it doesn't work.
I tried to find similar questions, I tried to see the order of css imports, I also tried to analise the loader module, the import order, but no clue about what is happening.

material design styles working on one module but not another

I've got a strange one and unfortunately I don't have code to share because I'm not sure it would add value to this post.
I have two components (User and Project). They both import Shared which is doing all of my material design imports and re-exporting them. On one of my modules when I apply the class mat-raised-button to an element, it works perfectly, on the other, it's only pulling in some of the CSS and injecting it into a <style> tag on the page but it's missing a bunch of other styles that are being injected to the working component on the working module. This has nothing to do with my style scopes because I can spin up a brand new component in both modules and it works great on one, not at all on the other (w/o doing anything other than adding the button with that class).
What's weird is that it IS pulling in some of the mat-raised-button styles, just not all of them. Is there some sort of mechanism that can prevent certain styles from being injected? I'm happy to post whatever code might help and I apologize that this is such a broad question, but unfortunately I don't have much to go on here...
UPDATE: I did some more digging and see that it's the mat-button class that's not working. I tried importing MatButtonModule directly into my failing module and seeing the same behavior...still not working correctly.
UPDATE 2: the buttons aren't working in my root app.component either. They're only working on one of my modules. This is really weird. Here's the code for the working module:
import { NgModule } from '#angular/core';
import { CommonModule } from '#angular/common';
import { SharedModule } from '../../Shared/Module/shared.module';
import { UserRegistrationComponent } from '../Components/user-registration.component';
import { CreateAccountDialogComponent } from '../Components/create-account-dialog.component';
import { FormsModule } from '#angular/forms';
import { ValidationErrorsComponent } from '../../user/Components/sharedcomponents/validation-errors.component';
import { UserLoginComponent } from '../Components/user-login.component';
#NgModule({
declarations: [
UserRegistrationComponent,
CreateAccountDialogComponent,
ValidationErrorsComponent,
UserLoginComponent
],
imports: [
CommonModule,
SharedModule,
FormsModule
],
entryComponents: [
CreateAccountDialogComponent
]
})
export class UserModule { }
and here's the code for the module that's not working:
import { NgModule } from '#angular/core';
import { CommonModule } from '#angular/common';
import { SharedModule } from '../../Shared/Module/shared.module';
import { ProjectFileUploadComponent } from '../Components/project-file-upload.component';
import { ProjectComponent } from '../Components/project.component';
import { FormsModule } from '#angular/forms';
import { MatButtonModule } from '#angular/material';
#NgModule({
declarations: [
ProjectFileUploadComponent,
ProjectComponent
],
imports: [
CommonModule,
SharedModule,
FormsModule,
MatButtonModule
]
})
export class ProjectModule { }
From what you're describing, I would think that you had local styles cancelling out global styles. The way to determine that is to comment out the styles in your local component, and see if your material design styles turn back up.
If that is the case, all you need to do is add and ViewEncapsulation.None statement to your #Component statement, to stop it cancelling out external styles:
#Component({
selector: 'speed-dial-fab',
templateUrl: './speed-dial-fab.component.html',
styleUrls: ['./speed-dial-fab.component.css'],
animations: speedDialFabAnimations,
encapsulation: ViewEncapsulation.None
})
So not a bugged module. Not voodoo either!

angular 6 component function doesnt work from HostListener from iframe

i have an angular 6 application, and a different web application inside in an iframe.
when i receive message from the iframe with the postmessage function, i do get to
the onMessage function, and then i do get to the openOffNet - but then the popup doesnt work.
it doesnt work when i activate the popup from a button.
anyone have any idea why it does not work?
thank you
import {HostListener, Component, OnInit } from '#angular/core';
import {NgbModal, NgbActiveModal} from '#ng-bootstrap/ng-bootstrap';
import {NgbdModalContentComponent} from '../ngbd-modal-content/ngbd-modal-content.component';
import {ModalAddDeviceComponent} from '../popupBoxes/modal-add-device/modal-add-device.component';
import {ModalAddOffnetComponent} from '../popupBoxes/modal-add-offnet/modal-add-offnet.component';
#Component({
selector: 'app-top-bar',
templateUrl: './top-bar.component.html',
styleUrls: ['./top-bar.component.css']
})
export class TopBarComponent implements OnInit {
constructor(private modalService: NgbModal) {
(<any>window).onMessage= this.onMessage.bind(this);
}
ngOnInit() {
}
#HostListener('window:message', ['$event'])
onMessage(e) {
console.log('HostListener');
if(e.data.function == 'subnetworkClicked'){
}
else{
console.log("open popUp");
this.openOffNet();
}
}
openOffNet () : void {
const modalRef = this.modalService.open(ModalAddOffnetComponent);
}
}
Try 'document:message' instead of 'window:message'

Angular Dynamic Components - Add Class and other attributes

I am using the following code for creating the dynamic components
import {
Component, OnInit, ViewContainerRef, ViewChild, ViewChildren,
ReflectiveInjector, ComponentFactoryResolver, ViewEncapsulation, QueryList, Input, AfterViewInit
} from '#angular/core';
import { Router, ActivatedRoute } from '#angular/router';
import { forEach } from '#angular/router/src/utils/collection';
import { IComponent } from 'app/app.icomponent';
#Component({
encapsulation: ViewEncapsulation.None,
selector: 'dynamic-component',
entryComponents: [HomeComponent, HighlevelSignalComponent],
template: `
<div #dynamicDiv [ngClass]="classFromMenu" >
<ng-template #dynamicComponentContainer></ng-template>
</div>
`,
styleUrls: [
'./dynamic-content.component.css'
],
})
export class DynamicComponent implements IComponent, OnInit, AfterViewInit {
classFromMenu: any;
#ViewChild('dynamicComponentContainer', { read: ViewContainerRef }) dynamicComponentContainer: ViewContainerRef;
constructor(private resolver: ComponentFactoryResolver, private route: Router,
private activatedRoute: ActivatedRoute, ) {
}
.......
buildComponent(passedData) {
// orderAndObjs has the data for creating the component
this.orderAndObjs.forEach(obj => {
var componentFactory = this.resolver.resolveComponentFactory(obj.component);
var compRef = this.dynamicComponentContainer.createComponent(componentFactory);
// compRef is the component that is created.
//Assuming the component that i am trying to create is <dynamic-component>.
//I want to add either a class or any other attribute like this
//<dynamic-component class="flex">
});
}
}
}
The dynamic-component is created perfectly fine and everything is working as expected. But the only issue is I want to add a class for dynamic-component so that it can be
<dynamic-component class="dynamicClass">
Any help is appreciated :(
Hmm.. I usually add it to the selector of component that is supposed to be an entryComponent ...
selector: 'dynamic-component.someclass',
^^^^^^^^^^^
to add attribute use attribute selector:
selector: 'dynamic-component[myattr=value]',
I call it hidden feature of entryComponents
but its declarative approach and can't be changed at runtime(indeed we can change it)
In Angular 5/6, using Renderer2 from #angular/core, you can do something like below:
constructor(private resolver: ComponentFactoryResolver, private route: Router,
private activatedRoute: ActivatedRoute, private renderer2: Renderer2) {
}
buildComponent(passedData) {
this.orderAndObjs.forEach(obj => {
var componentFactory = this.resolver.resolveComponentFactory(obj.component);
var compRef = this.dynamicComponentContainer.createComponent(componentFactory);
this.renderer2.addClass(compRef.location.nativeElement, 'flex');
});
}
High-level DOM operations are performed with Renderer2 provider. Considering that it was injected, it is:
this.renderer2.addClass(compRef.location.nativeElement, 'dynamicClass');
It should be noticed that depending on how dynamic element is attached to DOM, this may be unnecessary complication.
Considering that dynamicComponentContainer is real DOM element and not <ng-template>, the view of dynamic component can be directly mounted to the container, thus eliminating <dynamic-component> wrapper element:
Given the container:
<div class="dynamicClass" #dynamicComponentContainer></div>
It will be:
var compRef = componentFactory.create(
this.injector,
[],
this.dynamicComponentContainer.element.nativeElement
);

How to use #angular/animations in angular asp.net core 2 template?

I can't import #angular/animations in app.module.shared file:
When i import #angular/animations in app.module.browser file animations don't work.
What i need to do for use material animations in angular asp.net core 2 template?
I initially added the animations import into the app.module.shared file and I got the same error. To fix it I moved the import into the app.module.browser file.
This is how I left my module.browser:
import { NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { BrowserAnimationsModule } from '#angular/platform-browser/animations';
import { AppModuleShared } from './app.module.shared';
import { AppComponent } from './components/app/app.component';
#NgModule({
bootstrap: [ AppComponent ],
imports: [
BrowserModule,
BrowserAnimationsModule,
AppModuleShared
],
providers: [
{ provide: 'BASE_URL', useFactory: getBaseUrl }
]
})
export class AppModule {
}
export function getBaseUrl() {
return document.getElementsByTagName('base')[0].href;
}
Example of using BrowserAnimationsModule:
https://github.com/aspnet/JavaScriptServices/commit/c0c47e3def208873c470a524a826f8d235a5f9de
The error you posted is caused because#angular/animations uses DOM references, like document and window.
By default, ASP.NET Core MVC SPA apps created using the initial template will pre-render your Javascript code on serverside, as defined in your Index view.
<app asp-prerender-module="ClientApp/dist/main-server">Loading...</app>
So you can't add DOM references on server and shared modules.
You have to add #angular/animation to app.module.browser.ts and then use it on your submodules and/or components.

Resources