Call ngAfterViewInit of Angular component from common place - javascript

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;
}

Related

Reloading DOMContent

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
}
}

Executing some code when a service is initialized

Is it possible to execute some code when a service is initialized. For example when the service product Service is initialized I would want to execute this code:
this.var = this.sharedService.aVar;
Other than constructor there is NO lifecycle hooks for Service..
Lifecycle hooks are supported by component/directive
Injectables are normal classes (normal objects) and as such, they have no special lifecycle.
#Injectable()
export class SampleService {
constructor() {
console.log('Sample service is created');
//Do whatever you need when initialized.
}
}
the class’s constructor is called, so that’s what your “OnInit” would be. As for the destruction, a service does not really get destroyed.
where a service needs another service use dependency injection
#Injectable()
export class HeroService {
private yourVariable: any;
constructor(private sharedService: sharedService) {
this.yourVariable = this.sharedService.aVar;
}
I suggest you read up a little on lifecycle hooks in Angular. While in the constructor is an option it is a good idea to keep the code in that limited to variable initialization and use the ngOnInit lifecycle hook to do class initialization work. Both are an option but understanding the lifecycle hooks is a good starting point in addressing your question and thinking through where you want to do this.

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.

Is it possible to create a global attach() handler for viewmodels?

I have a use case with Aurelia where I would like to have a handler run for every view that is attached. (It's an HTML5 polyfill for date and number inputs that would work via querySelector.) I realize that I could call this within every view that I create, but I'm wondering if there's a best practice to set this at a global level. (Note: This could probably be done with a router pipeline step, but all views may not be subject to that, such as views loaded via compose.)
I realize that this could potentially be dangerous, but is there a best practice to add global attached() and detached() handlers for views and viewmodels?
Edit: Looking here (https://github.com/aurelia/templating/blob/ee5b9d6742fddf3d163aee8face6e6a58ba1554c/src/view.js#L259) it looks as though it would be possible to add a hook for a global handler here that took a view as an argument, but I'd rather not have to change the framework code if possible.
My idea would be to create a base viewmodel class with an attached logic, which would contain globally required functionality.
Extended viewmodels could call super.attached() to execute global logic as needed.
You can find a demo here: https://gist.run/?id=fea4069d8a4361c4802c7c5d42105145
This can work with compose as well. I know, it isn't a completely automated solution but an opt-in method, so it would require a bit of additional work on all viewmodels.
Base class - used by all viewmodels
import { inject } from 'aurelia-framework';
#inject(Element)
export class BaseView {
constructor(element) {
this.element = element;
}
attached() {
// global logic goes here
}
}
Example viewmodel - actual implementation
import { BaseView } from './base-view';
import { inject } from 'aurelia-framework';
#inject(Element)
export class ExtendedView extends BaseView {
constructor(element) {
super(element);
}
attached() {
super.attached(); // global logic runs
}
}

Observable isnt triggering

I have two components that should be connected via Observable().
In the first i am declaring
#Injectable()
export class One{
private changeConfirmed = new Subject<boolean>();
changeConfirmed$ = this.changeConfirmed.asObservable();
public init$: EventEmitter<boolean>;
registerChangeConfirmed(category:boolean){
alert('sending')
this.changeConfirmed.next(category);
}
onMsg(){
this.registerChangeConfirmed(true);
}
}
onMsg is event bound to template
and in the second one
Import { One } from './pat/to/it'
#Component({
providers:[One]
})
export class two{
constructor( private childClass : One ){
this.childClass.changeConfirmed$.subscribe( x => {alert(x)})
}
}
However the event does not get emitted.
But when i emit event in class two instead of class one = i include
this.childClass.registerChangeConfirmed(true)
in class two the event gets triggered. Why isn't it working when i invoke it from class one?
Don't provide the service One on the component. This way each component instance will get its own One instance. If you provide it at a common parent (AppComponent) then they will get a shared instance.
Angular2 DI maintains an instance (singleton) per provider.

Categories

Resources