How to handle undefined when passing data with Input in Angular? - javascript

A component colled with passing value with Input():
<app-available-modal [good]="'test'"></app-available-modal>
The component looks like:
#Component({
selector: 'app-available-modal',
templateUrl: './available-modal.component.html',
styleUrls: ['./available-modal.component.scss']
})
export class AvailableModalComponent implements OnInit {
#Input() good: TyreGoodModelTemp;
constructor() {
console.log(this.good);
}
ngOnInit(): void {
console.log(this.good);
}
}
I expect "test" and "test" output in the console.
And the console.log from ngOnInit() prints "test". But console.log from constructor() prints "undefined".
Why does it happen and how do I handle it?

In Angular the constructor function of a component class is being used for service injection only. Everything that is related to rendering and #Input resolving has to be handled in Angular's lifecycle hooks like ngOnInit and ngOnChanges

Don't try to access #Input()s in the constructor, do it in the ngOnInit life-cycle hook. The constructor has almost nothing to do with the Angular application life-cycle.
For more information : #8015757

Related

Angular template inheritance

Right now, I have dead simple use case of the inheritance, but for the life of me, I cannot get it to work.
The use case
My use case is thus:
I have BaseComponent that has some LoaderComponent. It has methods showLoader() and hideLoader() for showing it and hiding it, respectively.
Every loadable component, or even every component, extends this BaseComponent. Then, when there is network request, or we need to wait for a lot of stuff to render, we can just showLoader(), and then hideLoader() when the work is done.
Implementation
My implementation of this is pretty straightforward, but for some obscure reason, it's not working.
base.component.ts
//...
export class BaseComponent implements AfterViewInit {
#ViewChild(LoaderComponent) loader: LoaderComponent;
constructor() {}
ngAfterViewInit() {
// caveman debugging, I know. Even worse is that I found myself placing breakpoint on this line of caveman debugging. SAD!
console.log(this.loader);
}
showLoader() {
this.loader.show();
}
hideLoader() {
this.loader.hide();
}
}
base.component.html
I use transclusion here, of course.
<ng-content> </ng-content>
<app-loader #loader></app-loader>
contact.component.ts
//...
#AutoUnsubscribe
export class ContactComponent extends BaseComponent implements OnInit {
constructor(
private senderService: SenderService,
private messageSentService: MessageSentService,
private router: Router
) {
super();
// setup logic here...
}
ngOnInit() {}
sendEmail() // or whatever...
{
this.showLoader();
// send the email
// do the request
this.emailSender = this.senderService.send(this.emailMessage);
this.emailSender.subscribe((res) => {
this.hideLoader();
// handling the request here....
}
//...
}
contact.component.html
<app-base>
<!-- the contact page -->
</app-base>
When I fire this up, what I see, in the developer console, is :
.
When I place my breakpoint on the caveman debugging in BaseComponent.prototype.ngAfterViewInit(), it hit twice. On the first instance, I get this.constructor.name === "BaseComponent". However, on the second one, is the derived class : this.constructor.name === "ContactComponent".
The state of the decorated view child field loader isn't being passed down!!
How do I fix this, and without resorting to some bullshit design like making the derived class has-a base class?
Problem
From What I understand ur question is you are not only using just component inheritance but template composition as well
contact.component.html
<app-base>
<!-- the contact page -->
</app-base>
Explanation
Thing is You can not use Parent Template while inherit from it. a component has only one template.
in contact.component.html <app-base> means an instance of base-component as part of ur template.
so ur child component template is composed of base not inherit from it .
bcs Child Component template has not loader it it so its null.
Either use compose or inheritance
Simple Solution
while using component inheritance you have to copy all ur base template to child template
contact.component.html
<app-loader></app-loader>
Advance Solution
We can achieve almost what u want with composite pattern
start with just an interface
export interface ILoader{
showLoading ():void
hideLoading ():void
}
create a app-loader wrapper that implement ILoader
#Component({
selector: 'app-loader-wrapper',
template: `<ng-content> </ng-content>
<app-loader></app-loader>`,
styleUrls: ['./loader-wrapper.component.css']
})
export class LoaderWrapperComponent implements ILoader {
#ViewChild(Loader) loader: Loader
showLoading (){
this.loader.showLoading()
}
hideLoading (){
this.loader.hideLoading()
}
}
Create a baseComponent Class it will be base of all ur component which need loading note it has no template
class BaseComponent implements ILoader{
#ViewChild(LoaderWrapperComponent) loader: LoaderWrapperComponent
showLoading (){
this.loader.showLoading();
}
hideLoading (){
this.loader.hideLoading();
}
}
now create components and inherit from it as use composition in template
#Component({
selector: 'my-app',
template: `<app-loader-wrapper>
// rest of ur component
</app-loader-wrapper>`,
styleUrls: [ './app.component.css' ]
})
export class AppComponent extends BaseComponent {
}

Angular - Cannot get parent component data

I'm passing a function as parameter from parent to child component. When click event is occurred, function of parent component trigger, but all the property of parent component is undefined. For example,
Parent Component
export class AppComponent implements OnInit {
constructor( private notificationService: NotificationService ) {}
unreadNotification(): Observable<any> {
// here this.notificationService is undefined
console.log( this.notificationService );
}
}
Parent html
<notification-menu [unread]= "unreadNotification"></notification-menu>
child Component
export class NotificationMenuComponent implements OnInit {
#Input() updateUnread: Function;
}
child html
<button type="button" class="icon-button" (click)="updateUnread()">
</button>
Now when I click on notification button, unreadNotification is triggered, but value of this.notificationService in console.log is undefined.
How can I solve this?
You should use #Input() to pass values from parent to child and #Output() to pass values from child to parent.
Child HTML:
<button type="button" class="icon-button" (click)="update()">
</button>
Child Component:
export class NotificationMenuComponent implements OnInit {
#Output() updateUnread = new EventEmitter<string>();
update() {
this.updateUnread.emit("I am working man!");
}
}
Parent HTML:
<notification-menu (updateUnread)= "unreadNotification($event)"></notification-menu>
Parent Component:
export class AppComponent implements OnInit {
constructor( private notificationService: NotificationService ) {}
unreadNotification(dataFromChild: string) {
console.log(dataFromChild);
}
}
The answer from #nimeresam is good advice - using an #Output is an idomatic way to achieve this.
It's worth noting though, that the reason that your original solution doesn't work is due to the way that javascript handles the this context.
Writing (click)="updateUnread()" is equivalent to saying this.updateUnread() with this being NotificationMenuComponent - as notificationService does not exist on NotificationMenuComponent you get the undefined error.
To have the context of the parent component used, you would need to bind the context to the updateUnread function before passing it into the child component.
This can be achieved either by converting the function to be an arrow functionn, or using Function.bind
See:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Function/bind
It's normally a good idea to enable the typescript option for --noImplicitThis to help catch these errors (though unsure if it will detect it in this case)
You can use arrow function so that you can use parent component's information. You can try as like as given below.
updateUnreadNotification = () => {
// by using arrow function you can get notificationService information
console.log( this.notificationService );
}
Hope your problem will be solve by this.

Access element over the router-outlet Angular 6

<side-nav [navTitle]="navTitle"></side-nav>
<router-outlet>
</router-outlet>
I have navigation bar at the root component. I created [navTitle] with #Input Decorator inside the side-nav component. side-nav component is placed in another component(root-component). However I want access [navTitle] and change from component which loaded inside the router-outlet acording to which component is loaded. How do I achieve that?
You can't pass any data to router-outlet as to regular component (at the current version of Angular it's not possible, may be it will be added in the future), so the following syntax is invalid:
<router-outlet [dataToPass]="'something'"></router-outlet>
In provided case, you can use services to share data between your components, and I think, that using observable is the best way, because you will get the updated version of data realtime:
data.service.ts
// Other service stuff
#Injectable()
export class DataService {
private navTitle$: BehaviorSubject<string> = new BehaviorSubject<string>('Default nav title');
public setNavTitle(newNavTitle: string): void {
// Sets new value, every entity, which is subscribed to changes (`getNavTitle().subscribe(...)`) will get new value every time it changes
this.navTitle$.next(newNavTitle);
}
public getNavTitle(): Observable<string> {
// Allow to `subscribe` on changes and get the value every time it changes
return this.navTitle$.asObservable();
}
}
side-nav.component.ts
// Other component stuff
export class SideNavComponent implements OnInit, OnDestroy {
public navTitle: string = '';
private getNavTitleSubscription: Subscription;
constructor(private _dataService: DataService) { }
ngOnInit() {
// Will update the value of `this.navTitle` every time, when you will call `setNavTitle('data')` in data service
this.getNavTitleSubscription = this._dataService.getNavTitle()
.subscribe((navTitle: string) => this.navTitle = navTitle);
}
ngOnDestroy() {
// You have to `unsubscribe()` from subscription on destroy to avoid some kind of errors
this.getNavTitleSubscription.unsubscribe();
}
}
And any component, which is loaded in that router-outlet:
any.component.ts
// Other component stuff
export class SideNavComponent implements OnInit {
private navTitleToSet: string = 'Any title';
constructor(private _dataService: DataService) { }
ngOnInit() {
// Set title from current component
this._dataService.setNavTitle(this.navTitleToSet);
}
}
In such case you don't really need to pass the value from root component to side-nav, because you already have a subscription in side-nav component and you will have access to the latest value. If you need navTitle in both root and side-nav components, you can just move the logic with subscription to root.
And here is the working STACKBLITZ.
You can use a service to communicate between components. I have created a short example which would give you a glimpse of how it can be done.
The service being a singleton, has only one instance and hence the properties remain the same.
Hope it helps.
https://stackblitz.com/edit/angular-paziug?file=src%2Fapp%2Fapp.component.ts

How to pass template variable to component without ViewChild

I need to pass a component A to another component B.
Component B needs access to the nativeElement of A.
I managed to get it to work like this:
Container
Template
<component-a #componentA></component-a>
<component-b [origin]="reference"></component-b>
Controller
#ViewChild('componentA', {read: ElementRef}) reference: ElementRef;
Component B
#Input() origin: ElementRef;
Is there a way to get it to work without ViewChild, just with passing the template reference?
It should look like this:
<component-a #componentA></component-a>
<component-b [origin]="componentA"></component-b>
Right now if I do it like this I cannot access the nativeElement.
You can write the service class which can refer to the component and inject the service wherever you require to use the referred component.
#Component
class Component1 implements OnInit{
constructor(private ShareService : ShareService, private ref : ElementRef){
}
public ngOnInit(){
this.shareService.sharedComponent = this.ref;
}
}
ShareService {
public sharedComponent;
}
Better design would be to have sharedComponent as Observable.

Lifecycle hook that is called after data-bound properties of a directive are initialized (ngOnInit)

what does it mean "called after data-bound properties of a directive are initialized" in ngOnInit definition?
ngOnInit is a life cycle hook called by Angular 2 to indicate that Angular is done creating the component.
We need to import OnInit in order to use like this (actually implementing OnInit is not mandatory but considered good practice):
import {Component, OnInit} from '#angular/core';
then to use the method of OnInit we have to implement in the class like this.
export class ClassName implements OnInit{
constructor(){
//called first time before the ngOnInit()
}
ngOnInit(){
//called after the constructor and called after the first ngOnChanges()
}
}
ngOnInit is called right after the directive's data-bound properties
have been checked for the first time, and before any of its children
have been checked. It is invoked only once when the directive is
instantiated.
Please check this Link

Categories

Resources