How to call code inside ngAfterViewInit again in angular 2 - javascript

In my angular component I have ngAfterViewInit method which contains some statements that I want to execute after the view is initialized cause it contains some DOM manupulations.
My question is when some parameter is changed I want to run the code inside ngAfterViewInit.

You could define an EventEmitter in the constructor, subscribe to it in the ngAfterViewInit() function to update those values, emit it in the ngAfterViewInit() function, and then emit it again every single time you want to call it in subsequent areas of the component. Here is an example:
import { EventEmitter } from '#angular/core';
export class MyComponent implements AfterViewInit {
public myEvent: EventEmitter<void>;
public constructor() {
this.myEvent = new EventEmitter<void>();
}
public ngAfterViewInit(): void {
// This is how you call the function to do what you want to do with your DOM manipulations below. You can also call this exact function even from the HTML, if you wish to do so (see HTML example).
this.myEvent.emit();
this.myEvent.subscribe(
() => {
// Do whatever actions that you need to do here to perform your DOM manipulations.
},
(err: Error) => console.error(err);
);
}
public emitMyEvent(): void {
this.myEvent.emit();
}
}
<my-component (click)="myEvent.emit()"></my-component>
<!-- or -->
<my-component (click)="emitMyEvent()"></my-component>

If you want to execute those statements on every changes than it is better to write those in ngAfterViewChecked().From the docs:
A lifecycle hook that is called after the default change detector has completed checking a component's view for changes
A callback method that is invoked immediately after the default change detector has completed one change-check cycle for a component's view.
So it will be called on every subsequent changes.
More information can also be found on the Lifecycle Hooks#AfterView docs

If your parameter is available as an Observable, you can just subscribe to it in the ngAfterViewInitmethod. If your parameter is not yet available as Observable, I suggest you take a look at the BehaviourSubject class. With this, you can control when the Observable will emit a new value + It will be triggered with the last value when you subscribe to it

You declare ordinary class methords in the class body, and later define them in the context of ngAfterviewInit.
here's a simple use case example:
export class ViewtestComponent implements OnInit, AfterViewInit{
#ViewChild('someElementMewantedToDoAction') elRef: ElementRef;
constructor() { }
ngOnInit(): void {
}
changeVal;
ngAfterViewInit() {
this.changeVal= (useME) => {
// some action
this.elRef.nativeElement.innerText++;
}
}
Later, use the method in template as
// Do Action= Button value will increment 11, 12 13 ... on each button click.
<button class="button success" (click)="changeVal($emit)>10</button>

I have solved my problem by implementing OnChanges and call ngAfterviewInit for changes other than the firstchange.This way I will make sure that the view is initiallized. By the way the variable subjected to change(changed_var) holds data used in DOM manipulation.
ngOnChanges(changes: SimpleChanges){
if(!changes.changed_var.isFirstChange()){
this.ngAfterViewInit();
}
}

Related

Angular Directive doesn't receive changes from component made in ngOnDestroy method

There's an unexpected directive behavior after component was destroyed.
Directive didn't get changes made in ngOnDestroy method in component
component :
export class InfoButtonComponent implements OnDestroy {
display = false;
justMethod() {
this.display = true | false; // (whatever) works fine, directive recevied that display was changed
}
ngOnDestroy(): void {
this.display = false; // directive doesn't handle it
this.cdr.detectChanges();
}
}
<div
[show]="display"
>1</div>
directive:
export class TooltipDirective implements OnChanges {
#Input() show = false;
ngOnChanges(changes: SimpleChanges): void {
// get changes from component made in other methods
// doesn't get changes made in ngOnDestroy method
}
}
Seems to match what documentation tells for ngOnDestroy():
Called immediately before Angular destroys the directive or component.
I would put emphasis on "immediately" and wouldn't expect that another round of change detection is run afterwards.
It is not clear what you try to accomplish, but it sounds like a hack.

Angular - Elements that are under ngIf* doesn't render when ngOnInit() is called

I notice that when I have some elements under ngIf*, on ngOnInit() those elements don't exists even when the condition is met. How can I "catch" the moment when an element that is under ngIf* is rendered (I need to call some function on it)
use ngAfterViewInit()
ngAfterViewInit() is called after a component's view, and its children's views, are created. Its a lifecycle hook that is called after a component's view has been fully initialized.
export class AfterViewInitDemoComponent implements AfterViewInit {
ngAfterViewInit() {
console.log("---ngAfterViewInit() Demo---");
}
}
please check following links for more info
https://angular.io/api/core/AfterViewInit
https://www.concretepage.com/angular/angular-ngafterviewinit
If you use #ViewChild, try to use static: true parameter.
#ViewChild('myElem', { static: true }) myElem: MyElementType;

angular 2 equivalent of vuejs v-bind

As title implies i'm looking for a way to bind an object with multiple properties to component #Inputs without having to explicitly write all of them
Let's say I have this object
let params = {
delta: 0.2,
theta: 2.3,
sigma: 'foo',
}
Instead of having to bind all of them individually like this
<my-component
[delta]="params.delta"
[theta]="params.theta"
[sigma]="params.sigma"/>
I would like bind all of them at once.
<my-component [someDirectiveIDontKnow]="params"/>
How can i do this?
Found a link to a previously asked question but couldn't get that to work properly.
Edit:
I'm not asking how to bind #Inputs. Imagine that the component I'm rendering has 40 #Inputs and I'm NOT allowed to rewrite it to just accept one #Input that could contain all the params.
So writing a template that uses this component gets really ugly and big.
....
<my-component
[param1]="params.param1"
[param2]="params.param2"
[param3]="params.param3"
[param4]="params.param4"
[param5]="params.param5"
[param6]="params.param6"
[param7]="params.param7"
[param8]="params.param8"
[param9]="params.param9"
[param10]="params.param10"
[param11]="params.param11"
[param12]="params.param12"
[param13]="params.param13"
[param14]="params.param14"
... and so on ....
/>
....
In my opinion, It would be best to define them all in a model
You would start with the following model
params.model.ts
import {SomeOtherModel} from './some-other.model'
export interface ParamsModel {
paramName1: string;
paramName2?: string;
paramName3?: number;
paramName4: SomeOtherModel;
}
Then in your component, you can force your input to take a specific model argument
my.component.ts
import {ParamsModel} from './params.model';
#Component({..})
class MyComponent {
#Input() params: ParamsModel;
}
app.component.html
<my-component params="paramsModel"></my-component>
app.component.ts
import {ParamsModel} from './params.model';
#Component({..})
class AppComponent implements OnInit {
paramsModel: ParamsModel;
ngOnInit(): void {
this.paramsModel = <ParamsModel>{someValue: someValue};
}
}
this way you have full code completion.
do note though! Angular does not deepwatch the contents, so changing the contents inside the Params object, will still have the same object ID in javascript, causing angular to not see the changes.
There are a few work-around for this
1: Bind every param (this is exactly what you do not want)
2: When changing contents of the model, destroy the instance and create a new instance everytime, you could do this by adding a constructor in the model and convert it olike this code
export class ParamsModel {
paramName1: string;
paramName2?: string;
paramName3?: number;
paramName4: SomeOtherModel;
constructor(config?: ParamsModel) {
Object.assign(this, config);
}
}
// first init
this.paramsModel = new ParamsModel(<ParamsModel>{someValue: someValue});
// updated init
this.paramsModel = new ParamsModel(this.paramsModel);
this.paramsModel.changedValue = changedValue; // (could also use an extend function on original params model)
3: Create an observer with events and trigger update events on the other side
4: use ngDoCheck to perform your own check if the contents changed
There is no generic directive to pass input properties in Angular. However, Angular supports binding any valid JavaScript type be it objects, arrays or primitives.
In the template
<my-component [params]="params"/>
In the class you have to use the #Input decorator to mark an object as an input. You can access it's value in any of the lifecycle hooks, some shown below. Note that params will not be set inside the constructor as view binding is performed after the class is instantiated.
class MyComponent {
#Input()
params: any
constructor() { } // <-- params not set
ngOnChanges() { } // <-- anytime params changes
ngOnInit() { } // <-- once when the component is mounted
}

Calling a locally declared function in Angular's ngOnChanges

I know it's a rather basic question but i just can't seem to find a solution to this. Here it is:
I have an Angular Component, and in that component i have a function.
export class RolesListComponent implements OnInit, OnChanges {
#ViewChild(DxDataGridComponent) dataGrid: DxDataGridComponent;
ngOnChanges(changes: SimpleChanges): void {
this.refresh();
}
refresh(){
this.dataGrid.instance.refresh();
}
}
Calling the this.refresh() inside the onchanges doesn't work and gives an error that refresh is undefined.
What can i do to execute either the code inside the function directly in the onchanges, or the function itself.
According to the doc
ngOnChanges Respond when Angular (re)sets data-bound input properties
Simply put, if you have any #input properties and if u want to detect any changes to those properties, you can use ngOnChanges. Since i don't see any input properties in you component that may be why, ngOnchanges doesn't get execute

Declare class properties inside typescript callback method

I have a method that is subscribing to an event from a pub sub messaging service. In the callback I am wanting to define a class property. When I try to assign the property value, it returns as undefined. I understand that the reference to 'this' changed from the class to the method, but I need it to have access to the class's 'this' property. How can I assign the value to the class property 'this.icon' inside my callback method?
import { Component, OnInit, OnDestroy } from '#angular/core';
import { Subscription } from 'rxjs/Subscription'
import { Events } from '../shared/messages/events';
import { MessageService } from '../shared/messages/message.service';
export class Component implements OnInit, OnDestroy {
public icon: string;
private subscription: Subscription;
constructor() { this.btnClickSubscribe();}
private btnClickSubscribe(): void {
this.subscription = this.messageService
.subscribe(Events.btnClick, (payload) => {
this.icon = 'fa-trash-o';
console.log(this.icon) //logs the correct value, 'fa-trash-o'
//but it's only available inside this context. I need it in the
//class context
});
}
Since this is an asynchronous event, this.icon will initially be undefined outside the callback, no matter what you do. Check this one about more info: How do I return the response from an Observable/http/async call in angular2?
You mentioned you are passing icon to a child via #Input() then make use of ngOnChanges in the child, which captures the changes happening to icon. In the ngOnChanges you can make a condition, that executes whatever logic you want to do, after the value has been set to to icon, so something like this in your child:
#Input() icon;
ngOnChanges() {
if(this.icon) {
console.log('icon is set, now do something with it!')
}
}
And if you have problems with the view, there are some possible solutions, like using the safe navigation operator, more info here: Cannot read property "totalPrice" of undefined
Here's a
Demo
Hope this helps! :)

Categories