Angular 2 : ViewChild is undefined on parent - javascript

I have following error :
Error: Uncaught (in promise): TypeError: Cannot set property 'test_id' of undefined
TypeError: Cannot set property 'test_id' of undefined
The errror is trigger on this line:
console.log('after view init testList', this.productList.test_id);
I saw a lot of posts but all of them seems outdated and most of them are saying that I have to use a function ngAfterViewInit which I did.
I have a clic action that triggers updateTestId and I want to pass this id to my child view ProductListComponent.
Here is my parent component :
import { Component,ViewChild} from '#angular/core';
import {Test,TestData} from './testService';
import { LocalDataSource } from 'ng2-smart-table';
import {ProductListComponent} from '../../components/product/list/productList.component';
#Component({
selector: 'test-list',
templateUrl: './testList.html',
styleUrls: ['./testList.scss']
})
export class TestListComponent{
//tests: Test[];
tests: any[];
selected_test : number;
#ViewChild(ProductListComponent)
private productList: ProductListComponent;
constructor(protected service: TestData){
this.service.getData().then((data) => {
this.tests = data.tests;
this.source.load(data);
});
}
settings = {
editable : false,
actions: {
add:false,
edit:false,
delete:false
},
columns: {
id: {
title: 'ID',
type: 'number'
},
nb_cartons: {
title: 'Cartons',
type: 'number'
},
nb_items: {
title: 'Items',
type: 'number'
},
nb_pallets: {
title: 'Pallets',
type: 'number'
},
};
//source : Test[];
source: LocalDataSource = new LocalDataSource();
public updateTestId(value:any):void {
this.selected_test = value.data.id;
console.log('rowSelect', this.selected_test );
//console.log(this.productList.test_id)
}
}
And here is my child component :
import { Component,Input,Output, OnChanges, SimpleChanges } from '#angular/core';
import { LocalDataSource } from 'ng2-smart-table';
import {Test} from '../../test/testService';
#Component({
selector: 'product-list',
templateUrl: './productList.html',
styleUrls: ['./productList.scss']
})
export class ProductListComponent implements OnChanges{
#Input() test_id : number = null;
settings = {
editable : false,
actions: {
add:false,
edit:false,
delete:false
},
columns: {
id: {
title: 'ID',
type: 'number'
},
sku: {
title: 'SKU',
type: 'string'
},
reference: {
title: 'Reference',
type: 'string'
},
size: {
title: 'Size',
type: 'string'
},
},
edit: {
editButtonContent: '<i class="ion-edit"></i>',
saveButtonContent: '<i class="ion-checkmark"></i>',
cancelButtonContent: '<i class="ion-close"></i>',
}
};
source: LocalDataSource = new LocalDataSource();
constructor() {
}
ngOnChanges(changes: SimpleChanges) {
console.log(changes['test_id'],changes['test_id'].currentValue);
console.log('change in child',changes.test_id);
}
/*
#Input()
set _test_id(id: number) {
this._test_id = id;
}
get test_id(): number { return this._test_id; }
*/
}
i use the child selector like this :
<test-list></test-list>
<product-list #productList [test_id]="selected_test"></product-list>

Add a template reference variable name to the product-list in the template
<product-list #productList [test_id]="selected_test"></product-list>
And reference it by that in the component
#ViewChild('productList')
private productList: ProductListComponent;
EDIT:
In this case Vivek Doshi is correct in his answer that you don't need it since you are passing data to the child via #Input. But still - if you want to use ViewChild, this is a solution :)

If you want to pass data from parent to child you can directly pass
<product-list [test_id]="selected_test"></product-list>
That's it , nothing more.
There is no need to access it via #ViewChild
To detect the changed value over the time from product-list component
ngOnChanges(changes: SimpleChanges) {
console.log(changes['test_id'].currentValue);
}
Example :
#Component({selector: 'my-cmp', template: `...`})
class MyComponent implements OnChanges {
#Input()
prop: number;
ngOnChanges(changes: SimpleChanges) {
// changes.prop contains the old and the new value...
}
}
Things to note :
You need to implements OnChanges on child component.
For more detail please read this doc

Related

Get the Query parameter from the URL in to Component

I am trying to understand how I can get the Query parameter from the URl in to my Component. Below is what I tried, I set the route in the app-routing.module.ts like
{
path: 'ProjectShipment/userId/231',
component: ProjectShipmentComponent,
data: { title: 'Project Shipment' },
}
And with in the project-shipment.component.ts I tried like
import {Router, ActivatedRoute, Params} from '#angular/router';
export class ProjectShipmentComponent implements OnInit {
constructor( private activatedRoute: ActivatedRoute) { }
ngOnInit() {
debugger;
this.activatedRoute.queryParams.subscribe(params => {
const userId = params['userId'];
console.log(userId);
});}}
When I debug it I get undefined in the logs
What am I missing here
You need to change your route to
{
path: 'ProjectShipment/:userId',
component: ProjectShipmentComponent,
data: { title: 'Project Shipment' },
}
Then when you call it like yourhost/projectshipment/231 in your component
this.activatedRoute.params.subscribe(params => {
const userId = params['userId'];
console.log(userId);
})
to get queryparams you code is right but your route should
{
path: 'ProjectShipment',
component: ProjectShipmentComponent,
data: { title: 'Project Shipment' },
}
and url should be yourhost/projectshipment?userid=231

how to pass more than two parameters to modal ng2 bootstrap

I have to pass a flag in my Dialog Component. It must be like this
this.dialogService.addDialog(ModalDialogComponent, { title: 'History', message: this.comments, isHistoryModel:true});
My ModalDialogComponent:
export class ModalDialogComponent extends DialogComponent < ModalDialogModel, null > implements ModalDialogModel {
title: string;
message: any;
isHistoryModel: boolean;
constructor(dialogService: DialogService) {
super(dialogService);
}
}
Error snapshot:
You have to add
isHistoryModel:boolean
into your
ModalDialogModel

How to show heading based on value in angular?

I am trying to change the title based on item._id .i stored item in component.
this is my html
<h1 mat-dialog-title>{{item._id ? "Update" : "Add"}} animal</h1>
below is my dialog-overview-example.ts
import {Component, Inject} from '#angular/core';
import {MatDialog, MatDialogRef, MAT_DIALOG_DATA} from '#angular/material';
/**
* #title Dialog Overview
*/
#Component({
selector: 'dialog-overview-example',
templateUrl: 'dialog-overview-example.html',
styleUrls: ['dialog-overview-example.css'],
})
export class DialogOverviewExample {
animal: string;
name: string;
item:string;
constructor(public dialog: MatDialog) {}
openDialog(): void {
item =[{"_id": "2","animal":"lion","weiht":"100"}];
let dialogRef = this.dialog.open(DialogOverviewExampleDialog, {
width: '250px',
data: { name: this.name, animal: this.animal,item: this.item }
});
dialogRef.afterClosed().subscribe(result => {
console.log('The dialog was closed');
this.animal = result;
});
}
}
#Component({
selector: 'dialog-overview-example-dialog',
templateUrl: 'dialog-overview-example-dialog.html',
})
export class DialogOverviewExampleDialog {
constructor(
public dialogRef: MatDialogRef<DialogOverviewExampleDialog>,
#Inject(MAT_DIALOG_DATA) public data: any) { }
onNoClick(): void {
this.dialogRef.close();
}
}
Demo
if _id is there in item heading should show update animal otherwise it should show Add animal in our case id already there in item so it should show update animal ..help me out
I could see that you have defined the item inside the component as an array. So you have to use the following code in HTML
<h1 mat-dialog-title>{{item[0]._id ? "Update" : "Add"}} animal</h1>

Cannot find namespace error for model in Angular2/TypeScript

The FeaturedCategories model
export class FeaturedCategories {
categories: Array<{ id: number, title: string, graphic: string, categorycards: Array<{}> }>;
}
Also tried this:
export class FeaturedCategories {
id: number;
title: string;
graphic: string;
categorycards: Object[];
}
The Component
import { Component, ChangeDetectionStrategy, ViewEncapsulation } from '#angular/core';
import { ApiService } from '../shared/services/api.service';
import { FeaturedCategories } from '../shared/models/home/featuredcategories';
#Component({
changeDetection: ChangeDetectionStrategy.Default,
encapsulation: ViewEncapsulation.Emulated,
selector: 'home',
styleUrls: [ './home.component.css' ],
templateUrl: './home.component.html'
})
export class HomeComponent {
testFeaturedCategories: Array<FeaturedCategories>;
constructor(private api: ApiService) {
// we need the data synchronously for the client to set the server response
// we create another method so we have more control for testing
this.universalInit();
}
universalInit() {
console.log('universalInit...')
this.api.getFeatured()
.subscribe(categories => {
console.log('categories', categories);
this.testFeaturedCategories = categories
});
}
}
This will work: testFeaturedCategories: Array<{}>;
However I'm trying to use TypeScript to let my App know what type of model to expect.
This causes the error above:
testFeaturedCategories: FeaturedCategories.categories;
And if I just try this: testFeaturedCategories: FeaturedCategories;
I get a type [{}] is not assignable error.
UPDATE
So I noticed that when I commented out all the keys in my FeaturedCategories model finally the error goes away and
featuredCategories: FeaturedCategories[]; will work.
However now this is just an empty object without keys to expect :(
export class FeaturedCategories {
// id: number;
// title: string;
// graphic: string;
// categorycards: Object[];
}
this is working fine for me.
export class MyComponent {
categories: FeaturedCategories[] = [{
id: 1,
title: "",
graphic: "",
categorycards: [{}]
}];
}
export class FeaturedCategories{
id: number;
title: string;
graphic: string;
categorycards: Object[];
}
My problem was trying to type my Array, instead of just using the Typed objects that exist in the larger Array.
Also had a problem in my service, originally I had this:
/**
* Get featured categories data for homepage
* /wiki
*/
getFeatured(): Observable<[{}]> {
return this.http.get(`${this.getFeaturedUrl}/home`)
// .do(res => console.log('getFeatured res', res.json()))
.map(res => res.json())
.catch(this.handleError);
}
I did not need or could even use a type for my larger Categories array, what I needed was a smaller type for the exact Objects that exist in that larger Array:
export class FeaturedCategory {
id?: number;
type: string;
title: string;
graphic?: string;
video?: string;
categorycards: Array<{}>;
}
So now with the correct Type of Objects inside my Array I added it to the service:
getFeatured(): Observable<[FeaturedCategory]> {
return this.http.get(`${this.getFeaturedUrl}/home`)
.map(res => res.json())
.catch(this.handleError);
}
Now back in my Component I imported the single Typed Object
import { FeaturedCategory } from '../shared/models/home/featuredcategory';
Then typed the variable:
featuredCategories: Array<FeaturedCategory>;
And finally in ngOnInit
ngOnInit() {
this.api.getFeatured()
.subscribe(categories => {
console.log('categories', categories);
this.featuredCategories = categories;
});
}
No more errors :)

Angular2 call function from another component

I have two components: NgbdAlertCloseable and AlertCtrl. Also I have AppComponent as parent component. What I want is to click a button in AlertCtrl component and create the alert on NgdbAlertCloseable component.
addSuccess() function adds an alert to the view and it worked well while I call it inside of its component. However, I tried to use an EventEmitter to call this function from another component (as suggested here: https://stackoverflow.com/a/37587862/5291422) but it gives this error:
ORIGINAL EXCEPTION: TypeError: self._NgbdAlertCloseable_2_4.addSuccess is not a function
Here are my files:
ngbd-alert-closeable.component.ts
import { Input, Component } from '#angular/core';
#Component({
selector: 'ngbd-alert-closeable',
templateUrl: './app/alert-closeable.html'
})
export class NgbdAlertCloseable {
#Input()
public alerts: Array<IAlert> = [];
private backup: Array<IAlert>;
private index: number;
constructor() {
this.index = 1;
}
public closeAlert(alert: IAlert) {
const index: number = this.alerts.indexOf(alert);
this.alerts.splice(index, 1);
}
public static addSuccess(alert: IAlert) {
this.alerts.push({
id: this.index,
type: 'success',
message: 'This is an success alert',
});
this.index += 1;
}
public addInfo(alert: IAlert) {
this.alerts.push({
id: this.index,
type: 'info',
message: 'This is an info alert',
});
this.index += 1;
}
}
interface IAlert {
id: number;
type: string;
message: string;
}
alert-ctrl.component.ts
import { EventEmitter, Output, Component } from '#angular/core';
import { NgbdAlertCloseable } from './ngbd-alert-closeable.component';
#Component({
selector: 'alert-ctrl',
template: '<button class="btn btn-success" (click)="addSuccessMsg()">Add</button>'
})
export class AlertCtrl {
#Output() msgEvent = new EventEmitter();
public addSuccessMsg(){
this.msgEvent.emit(null);
}
}
app.component.ts
import { Component } from '#angular/core';
#Component({
selector: 'my-app',
template: '<div class="col-sm-4"><alert-ctrl (msgEvent)="ngbdalertcloseable.addSuccess()"></alert-ctrl><ngbd-alert-closeable #ngbdalertcloseable></ngbd-alert-closeable>'
})
export class AppComponent { }
Am I using it wrong? How can I fix that?
Check that the addSuccess function is static and is using non static properties.
Should be:
public addSuccess(alert: IAlert) {
this.alerts.push({
id: this.index,
type: 'success',
message: 'This is an success alert',
});
this.index += 1;
}
And in your view you must pass the IAlert value in this example we'll send that value when we call msgEvent.emit(IAlert).
import { Component } from '#angular/core';
#Component({
selector: 'my-app',
template: '<div class="col-sm-4"><alert-ctrl (msgEvent)="ngbdalertcloseable.addSuccess($event)"></alert-ctrl><ngbd-alert-closeable #ngbdalertcloseable></ngbd-alert-closeable>'
})
export class AppComponent { }
Then you must send that IAlert, I'll change your AlertCtrl just for demo purpose.
import { EventEmitter, Output, Component } from '#angular/core';
import { NgbdAlertCloseable } from './ngbd-alert-closeable.component';
#Component({
selector: 'alert-ctrl',
template: '<button class="btn btn-success" (click)="addSuccessMsg()">Add</button>'
})
export class AlertCtrl {
currentAlert:IAlert = {id: 0, type: 'success', message: 'This is an success alert'};
#Output() msgEvent = new EventEmitter<IAlert>();
public addSuccessMsg(){
this.msgEvent.emit(this.currentAlert);
}
}
Good luck and happy coding!

Categories

Resources