Reloading DOMContent - javascript

I understand angular is one page application with multiple components and use route to interact between pages.
We have an angular app like that. One of the requirements is that on a specific component we need to add an eventListener to DomContentLoaded event.
Since the index.html page has been loaded (hence DomContentLoaded event has been fired) way before, we have a problem.
I cannot find a way to re-trigger the DomContentLoaded event.

The DomContentLoaded event fired before the first component was created. You can however use the ngAfterViewInit() method.
ngAfterViewInit() is a callback method that is invoked immediately after Angular has completed initialization of a component's view. It is invoked only once when the view is instantiated.
class YourComponent {
ngAfterViewInit() {
// Your code here
}
}
If you really need to catch the DomContentLoaded event anyway, do it in the main.ts file.

I don't know exactly, why you need to listen to DomContentLoaded in angular, because there are plenty of functions there, depends on which Version of angular do you use.
export class SomeComponent implements AfterViewInit {
ngAfterViewInit(): void {
}
}
But, you can retrigger an rendering of an component with *ngIf (Angular2+) or ng-if (AngularJS). Just put a boolean there and switch the boolean on an event.

Please try not use such listeners, they are (often) not needed in Angular. Try to use Component lifecycle hooks.
In your case, I recommend using ngAfterViewChecked(), which fires when the component finished rendering.
import { Component, AfterViewChecked } from '#angular/core';
#Component({
...
})
export class WidgetLgFunnelsComponent implements AfterViewChecked {
ngAfterViewChecked() {
console.log('Component finished rendering!')
}
}

If you want to do something with the DOM elements inside the template of the component, and you want to make sure they are there, you should use the ngAfterViewInit hook from angular:
#Component({
// ...
})
export class SomeComponent implements AfterViewInit {
ngAfterViewInit(): void {
// here you can be sure that the template is loaded, and the DOM elements are available
}
}

Related

How to prevent change detection on scroll? ( Angular )

I have a large component, and I would like to avoid unnecessary change detections to increase performance.
I added changeDetection: ChangeDetectionStrategy.OnPush to the #Component.
I would like to update the style of an element if it is scrolled. Unfortunately Angular runs the change detection, every time I scroll that element.
I tried to add an event listener to the native element to avoid this, but the change detection is still running again when I scroll:
#ViewChild('my_element') my_element_ref: ElementRef;
ngOnInit() {
this.my_element_ref.nativeElement.addEventListener('scroll', this.updateStyle);
}
ngAfterViewChecked() {
console.log('check');
}
The ngAfterViewChecked is called even if this.updateStyle is an empty function.
But if I comment out this.my_element_ref.nativeElement.addEventListener('scroll', this.onScroll), then ngAfterViewChecked is not called anymore.
How is it possible to call a function when an element is scrolled, but avoid Angular's change detection?
I would suggest you use ngZone.runOutsideAngular.
constructor (private zone : NgZone) {}
ngAfterViewInit () : any {
this.zone.runOutsideAngular(() => {
window.addEventListener('scroll', (e)=> {
console.log( 'scroll event fired' );
});
});
}
You might want to look at ChangeDetectorRef API.
Specifically you would detach the change detection for the component using detach() (maybe in the constructor like below) and then mark component for change in your scroll function via markForCheck().
constructor(public cd: ChangeDetectorRef) {
this.cd.detach();
}
this.cd.markForCheck();
Below links for your reference.
https://blog.angularindepth.com/everything-you-need-to-know-about-change-detection-in-angular-8006c51d206f
https://angular.io/api/core/ChangeDetectorRef
just inject NgZone in your constructor and then subscribe to an event like this:
this.ngZone.runOutsideAngular(() => {
....addEventListener('...);
});
keep in mind that change detection will not be called in this case, so you won't be able to change styles with template binding. It should be changed by native javascript or using Renderer2

Call ngAfterViewInit of Angular component from common place

I am using bootstrap tooltip.
I want to trigger ngAfterViewInit for every component from some common place.
Common place(like app.module.ts)
ngAfterViewInit() {
$('[data-toggle="tooltip"]').tooltip();
}
Component_1:
There is no ngAfterViewInit in component, still life cycle is trigger from some common place.
Component_2:
This component has ngAfterViewInit,
ngAfterViewInit() {
// Some extra functionality
}
In this case ngAfterViewInit is triggered from both the places(common place as well as from it's respective component)
Is there any way to achieve this?
Thanks
There isn't "one common place" where you can define a global behavior for all of your components.
Instead, you can create the following class and make your desired components extend it:
export abstract class BaseComponent implements AfterViewInit {
/**
* View initialization behavior. Note that derived classes must not implement AfterViewInit .
* Use viewInit() on derived classes instead.
*/
ngAfterViewInit(): void {
// Your code
this.viewInit();
}
/**
* Function that allows derived components to define an additional view initialization behavior
*/
abstract viewInit(): void;
}

When does angular 2 call onDestroy on a component?

I am not able to figure out when does the angular decide to call onDestory event?
When I toggle the component with *ngIF directive, onDestory is not called and the state of the component is maintained as if it is using the same instance of the component?
Can anybody elaborate as to when angular(2) destroys component? And how to achieve newer instance of component when toggling with *ngIf?
Most DOM manipulations in Angular are performed using ViewContainerRef. In particular, this mechanism is used internally by ngIf:
private _updateView() {
...
this._viewContainer.clear();
...
this._thenViewRef =
this._viewContainer.createEmbeddedView(this._thenTemplateRef, this._context);
}
}
and router-outlet:
#Directive({selector: 'router-outlet', exportAs: 'outlet'})
export class RouterOutlet implements OnDestroy, OnInit {
constructor(..., private location: ViewContainerRef, ...)
detach(): ComponentRef<any> {
...
this.location.detach();
...
}
attach(ref: ComponentRef<any>, activatedRoute: ActivatedRoute) {
...
this.location.insert(ref.hostView);
}
Whenever either viewContainer.clear() or viewContainer.remove(index) method is called the relevant components or embedded views (created with ng-template) are removed and ngOnDestroy lifecycle hook is called on them.
Can anybody elaborate as to when angular(2) destroys component?
This will happen when using structural directives like ngIf and ngFor, when router navigates away from current router-outlet directive or when you manually remove dynamic components or embedded views using viewContainerRef.
You can read more about DOM manipulation using ViewContainerRef in:
Exploring Angular DOM manipulation techniques using ViewContainerRef

How can I get child component names as they or after they initialize in Angular?

I've been studying Angular's lifecycle hooks while looking for a way to know when and which child components are loaded.
I see that ngAfterViewInit()
"Responds after Angular initializes the component's views and child
views."
Since ngAfterViewInit knows about the children, how could I get their identifying information as they (or after) they initialize?
Something like this pseudo code:
// ngAfterViewInit() - Respond after Angular initializes the component's views and child views.
export class AppComponent implements AfterViewInit {
ngAfterViewInit() {
console.log(‘Children should be loaded');
// loop through ngAfterViewInit
querySelector('body').classList.add(ngAfterViewInit[i].componentName);
}
}
sidenote: the goal is to add component names or selector names to <body>'s class list.

jQuery function not called at page load in Angular 2

I am trying to call jQuery function (i.e. initialize bootstrap selectpicker) at page load in ng2 app:
jQuery(this.elementRef.nativeElement).find("select").selectpicker({
iconBase: 'fa',
tickIcon: 'fa-check',
title: '...'
});
(assume that jQuery and elementRef are defined and it works well in other cases)
I try to call this function in ngOnInit, ngAfterContentInit, ngAfterViewInit but with no luck.
I suspect it calls before element is rendered or so.
Here is my template of select which I try to convert into bootstrap selectpicker:
<select ngControl="hashtags" [(ngModel)]="hashtags" multiple>
<option *ngFor="#hashtag of card.hashtags" [value]="hashtag.id" (click)="selectHashtag(hashtag.id)">{{hashtag.name}}</option>
</select>
Any ideas how to call it right?
UPD: seems like the issue comes from a fact that a form (and my select element) is rendered inside ngIf:
<div *ngIf="card">
...
</div>
so, at the moment of rendering page and calling ngOnInit() form is not rendered yet.
I will try to work it around by applying different approach, but how would you call function AFTER http request and form render are complete?
Wouldn't it be similar to this:
import {Component, ElementRef, OnInit} from 'angular2/core';
declare var jQuery:any;
#Component({
selector: 'jquery-integration',
templateUrl: './components/jquery-integration/jquery-integration.html'
})
export class JqueryIntegration implements OnInit {
elementRef: ElementRef;
constructor(elementRef: ElementRef) {
this.elementRef = elementRef;
}
ngOnInit() {
jQuery(this.elementRef.nativeElement).draggable({containment:'#draggable-parent'});
}
}
More info here: http://www.syntaxsuccess.com/viewarticle/using-jquery-with-angular-2.0
and the right answer is to call it inside ngAfterViewChecked() {}
AfterViewChecked callback triggers everytime page is changed, thus to make sure my functions are called only once I set flags i.e. selectpickerEnabled = true and check them before calling function again.
I believe there must be the right way to do it, but that's how I am going to solve it for now...

Categories

Resources