Angular: How to call constructor of dynamically created component? - html

Can someone help me out how to pass on a value for the constructor of a component which is created dynamically?
This is how the component FilterComponent is created:
import { Component, ComponentFactory, ComponentFactoryResolver, ComponentRef, OnInit, ViewChild, ViewContainerRef } from '#angular/core';
import { FilterComponent } from '../filter/filter.component';
export enum FilterType {
DateRangeFilter, SensorSelectFilter
}
#Component({
selector: 'app-filter-collection',
templateUrl: './filter-collection.component.html',
styleUrls: ['./filter-collection.component.css']
})
export class FilterCollectionComponent implements OnInit {
filters: Array<ComponentRef<FilterComponent>> = [];
#ViewChild("messagecontainer", { read: ViewContainerRef }) entry!: ViewContainerRef;
constructor(private resolver: ComponentFactoryResolver) { }
onAddDateRangeFilter() {
const factory: ComponentFactory<FilterComponent> = this.resolver.resolveComponentFactory(FilterComponent);
const filter = this.entry.createComponent<FilterComponent>(factory);
this.filters.push(filter);
}
ngOnInit(): void {
}
}
and this is the component:
#Component({
selector: 'app-filter',
templateUrl: './filter.component.html',
styleUrls: ['./filter.component.css']
})
export class FilterComponent {
constructor(private type : FilterType) {
}
ngOnInit(): void {
}
}

I am not sure if there is a way to access the constructor. But there is a way to get the actual instance of your dynamically generated component
import { Component, ComponentFactory, ComponentFactoryResolver, ComponentRef, OnInit, ViewChild, ViewContainerRef } from '#angular/core';
import { FilterComponent } from '../filter/filter.component';
export enum FilterType {
DateRangeFilter, SensorSelectFilter
}
#Component({
selector: 'app-filter-collection',
templateUrl: './filter-collection.component.html',
styleUrls: ['./filter-collection.component.css']
})
export class FilterCollectionComponent implements OnInit {
filters: Array<ComponentRef<FilterComponent>> = [];
#ViewChild("messagecontainer", { read: ViewContainerRef }) entry!: ViewContainerRef;
constructor(private resolver: ComponentFactoryResolver) { }
onAddDateRangeFilter() {
const factory: ComponentFactory<FilterComponent> = this.resolver.resolveComponentFactory(FilterComponent);
const filter:ComponentRef<FilterComponent> = this.entry.createComponent<FilterComponent>(factory);
this.filters.push(filter);
// you can use the componentRef.instance to reference any variables inside your dynamically generated component
filter.instance.type = FilterType.DateRangeFilter
}
ngOnInit(): void {
}
}

Related

Angular 9 Parent Child Call Function

My parent component call function on two childs components.
I have an error on the second component.
Parent Component
import { Component, Input, ViewChild } from '#angular/core';
import { EchartsAreaStackComponent } from './echart/echarts-area-stack.component';
import { AccordionComponent } from './accordion/accordion.component';
#Component({
selector: 'hg-waitstats',
styleUrls: ['./waitStats.component.scss'],
templateUrl: './waitStats.component.html',
})
export class WaitStatsComponent {
#ViewChild(EchartsAreaStackComponent) EchartsAreaStackComponent: EchartsAreaStackComponent ; //work
#ViewChild(AccordionComponent) AccordionComponent: AccordionComponent ; //not work
#Input() selectedLimit: String = '3';
ngAfterViewInit() {
// child is set
this.EchartsAreaStackComponent.changeLimit(this.selectedLimit); // work
this.AccordionComponent.getWaitStatData(this.selectedLimit); // not work
}
changeLimit(limit: any){
this.EchartsAreaStackComponent.changeLimit(limit); //work
this.AccordionComponent.getWaitStatData(limit); // not work
}
}
parent html
<div class="col-6">
<nb-card>
<nb-card-header>
<nb-select [selected]="selectedLimit" (selectedChange)="changeLimit($event)">
<nb-option value="5">Top 5</nb-option>
<nb-option value="3">Top 3</nb-option>
<nb-option value="1">Top 1</nb-option>
</nb-select>
</nb-card-header>
<nb-card-body>
<ngx-echarts-area-stack></ngx-echarts-area-stack>
</nb-card-body>
</nb-card>
</div>
Working component (EchartsAreaStackComponent)
import { AfterViewInit, Component, OnDestroy } from '#angular/core';
import { NbThemeService } from '#nebular/theme';
import { WaitStatsService } from '../../../../#core/backend/common/services/waitStats.service';
import { WaitType } from '../../../../#core/interfaces/common/waitStats';
import { UserStore } from '../../../../#core/stores/user.store';
import { User } from '../../../../#core/interfaces/common/users';
#Component({
selector: 'ngx-echarts-area-stack',
templateUrl: './echarts-area-stack.component.html',
styleUrls: ['./echarts-area-stack.component.scss'],
providers:[WaitStatsService]
})
export class EchartsAreaStackComponent implements AfterViewInit, OnDestroy {
options: any = {};
themeSubscription: any;
user: User;
legendData: WaitType[];
seriesData: {};
seriesX: {};
selectedLimit: String = '3';
constructor(
private theme: NbThemeService,
private service: WaitStatsService,
private userStore: UserStore
) {}
ngOnInit()
{
this.getWaitStatData(this.selectedLimit);
}
getWaitStatData(limit){
...
}
changeLimit(limit) {
if (this.selectedLimit !== limit) {
this.selectedLimit = limit;
this.getWaitStatData(this.selectedLimit);
}
}
ngAfterViewInit() {
...
}
ngOnDestroy(): void {
this.themeSubscription.unsubscribe();
}
}
Component that genere error (AccordionComponent)
import { Component } from '#angular/core';
import { WaitStatsCategoriesService } from '../../../../#core/backend/common/services/waitStatsCategories.service';
#Component({
selector: 'ngx-accordion',
templateUrl: 'accordion.component.html',
styleUrls: ['accordion.component.scss'],
providers:[WaitStatsCategoriesService]
})
export class AccordionComponent {
accordion;
selectedLimit: String = '3';
waitTypes: {};
constructor(
private service: WaitStatsCategoriesService
) {}
toggle() {
this.accordion.toggle();
}
ngOnInit()
{
this.getWaitStatData(this.selectedLimit);
}
getWaitStatData(limit){
this.service.getTopWaitType( limit )
.subscribe( (data) => {
this.waitTypes = data.waitTypes;
});
}
}
ERROR TypeError: Cannot read property 'getWaitStatData' of undefined
at WaitStatsComponent.ngAfterViewInit (:4200/app-pages-pages-module.js:208942)
at callHook (:4200/vendor.js:40119)
at callHooks (:4200/vendor.js:40083)
at executeInitAndCheckHooks (:4200/vendor.js:40024)
at refreshView (:4200/vendor.js:46856)
at refreshDynamicEmbeddedViews (:4200/vendor.js:48145)
at refreshView (:4200/vendor.js:46803)
at refreshComponent (:4200/vendor.js:48220)
at refreshChildComponents (:4200/vendor.js:46511)
at refreshView (:4200/vendor.js:46832)
You haven't add ngx-accordion in parent.html template. Thats why its breaking. Other component working as its available in your parent.html

Appending a translated string in html (angular 6) with a property of an object

I have a myapp.component.ts file in which I have the following
import { Component, Input, OnInit, ViewChild } from '#angular/core';
import { isNullOrUndefined } from 'util';
import { AuthenticationService } from '/services';
import * as _ from 'lodash';
#Component({
selector: 'cd-myapp',
templateUrl: './myapp.component.html',
styleUrls: ['./myapp.component.scss']
})
export class myAppComponent implements OnInit {
#Input() car: Car;
constructor(
private authenticationService: AuthenticationService) { }
ngOnInit() {
//my code
}
}
I have another component, in which I have the following in the carhandler.component.ts file :
import { Component, OnInit, Input, Output, EventEmitter } from
'#angular/core';
#Component({
selector: 'app-carhandler',
templateUrl: './carhandler.component.html',
styleUrls: ['./carhandler.component.scss']
})
export class CarhandlerComponent implements OnInit {
#Input() field: string;
#Input() value: string;
constructor() { }
ngOnInit() {
}}
In my myapp.component.html I would like to append. I tried this :
<app-carhandler [field]="Testing"
[value] = "'DESCRIPTION' | translate" +'-'+{{car.color}}>
</app-carhandler >
It does not work. How should I tackle this problem?
I'm sorry, i'm having no explanation why, but it works this way:
<app-carhandler [field]="Testing"
[value] = "(('DESCRIPTION' | translate) +'-'+ car.color)">
</app-carhandler >

Display data from json array using angular4

I am new to angular so please help me. I have an api returning an array of objects containing name, place id.
I need to display this in different cards on my html page, the cards being a widget.
in the parent component under the ngOnInit() section how do I access this json data and loop through the array in order to display it on my page as different cards?
Thank you in advance.
import { Component, OnInit } from '#angular/core';
import {HttpClient} from '#angular/common/http';
import { Observable } from 'rxjs/observable';
#Component({
selector: 'app-home-page',
templateUrl: './home-page.component.html',
styleUrls: ['./home-page.component.css']
})
export class HomePageComponent implements OnInit {
showSplash = true
//public events: any = [];
events = [];
constructor(private http : HttpClient) { }
ngOnInit() {
this.showSplash = true
this.http.get("/events").subscribe(data => {
console.log("EVENTS ARE: ", data);
this.events = data;
console.log(this.events)
})
}
ngAfterViewInit(){
setTimeout(() => {
this.showSplash = false
}, 3000);
}
}
This will get you the events you want.
import { Component, OnInit, OnDestroy } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { Subscription } from 'rxjs';
#Component({
selector: 'app-home-page',
templateUrl: './home-page.component.html',
styleUrls: ['./home-page.component.css']
})
export class HomePageComponent implements OnInit, OnDestroy {
showSplash = true
events = [];
subscription: Subscription;
constructor(private http: HttpClient) {}
ngOnInit() {
this.subscription = this.http.get("/events").subscribe(data => {
this.events = data;
this.showSplash = false;
});
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
You will have to implement a Child Component(EventComponent probably with the selector app-event) that will accept an event object as an #Input property. Then in your HomePageComponent Template, you can loop through the events like this:
<div *ngFor="let event of events">
<app-event [event]="event"></app-event>
</div>
Alternatively:
You can use the async pipe in your HomePageComponent's Template to avoid manually unsubscribing from the Observable Subscription. Your HomePageComponent Class code will change to:
import { Component, OnInit } from '#angular/core';
import { HttpClient } from '#angular/common/http';
#Component({
selector: 'app-home-page',
templateUrl: './home-page.component.html',
styleUrls: ['./home-page.component.css']
})
export class HomePageComponent implements OnInit {
events$;
constructor(private http: HttpClient) {}
ngOnInit() {
this.events$ = this.http.get("/events");
}
}
And then in HomePageComponent's Template:
<div *ngFor="let event of events$ | async">
<app-event [event]="event"></app-event>
</div>
Here's how your EventComponent would look like in this case:
import { Component, Input, OnChanges } from '#angular/core';
#Component({
selector: 'app-event',
templateUrl: './event.component.html',
styleUrls: ['./event.component.css']
})
export class EventComponent implements OnChanges{
#Input() event;
ngOnChanges() {
this.events$ = this.http.get("/events");
}
}

How to use one component data in another component in angular 6?

I have a component.ts file which is making a http call & retrieving json data as response. I need to use this response in another component.ts file. Can anyone tell me how to process this?
first component.ts:
#Component({
selector: 'app-cat',
templateUrl: './first.component.html',
styleUrls: ['./first.component.css']
})
export class firstComponent extends Lifecycle {
this.http.get('/name',{responseType:"json"}).subscribe(
response => {
console.log("data :"+response);
console.log("data stringify:"+JSON.stringify(response));
});
}
I need to use the json content which is in the response in my second component file. Can anybody tell me how to proceed this in angular6?
****Create separate service for making calls and in that service create a method as such
public getData(): Observable<> {
return this.http.get('/name',{responseType:"json"}).subscribe(
response => {
console.log("data :"+response);
console.log("data stringify:"+JSON.stringify(response));
});
}
****And in your component declare service in constructor //don't forget to import it
public jsonData:any;
constructor(private Service: Service ) {
}
getData() {
this.Service.getData().subscribe(data => {
console.log("Data is ",data);
this.jsonData = data;
},
error => console.log(error)
);
}
Finally,you can use jsonData to work with your data.
Parent to Child: Sharing Data via Input
parent.component.ts
import { Component } from '#angular/core';
#Component({
selector: 'app-parent',
template: `
<app-child [childMessage]="parentMessage"></app-child>
`,
styleUrls: ['./parent.component.css']
})
export class ParentComponent{
parentMessage = "message from parent"
constructor() { }
}
child.component.ts
import { Component, Input } from '#angular/core';
#Component({
selector: 'app-child',
template: `
Say {{ message }}
`,
styleUrls: ['./child.component.css']
})
export class ChildComponent {
#Input() childMessage: string;
constructor() { }
}
Sharing Data via Output() and EventEmitter
parent.component.ts
import { Component } from '#angular/core';
#Component({
selector: 'app-parent',
template: `
Message: {{message}}
<app-child (messageEvent)="receiveMessage($event)"></app-child>
`,
styleUrls: ['./parent.component.css']
})
export class ParentComponent {
constructor() { }
message:string;
receiveMessage($event) {
this.message = $event
}
}
child.component.ts
import { Component, Output, EventEmitter } from '#angular/core';
#Component({
selector: 'app-child',
template: `
<button (click)="sendMessage()">Send Message</button>
`,
styleUrls: ['./child.component.css']
})
export class ChildComponent {
message: string = "Hola Mundo!"
#Output() messageEvent = new EventEmitter<string>();
constructor() { }
sendMessage() {
this.messageEvent.emit(this.message)
}
}
please visit https://angularfirebase.com/lessons/sharing-data-between-angular-components-four-methods/ for other methods.
Solution 1 using a common injectible service
Shared.service.ts
#Injectible()
class SharedService {
function getData():any{
return this.http.get('/name',{responseType:"json"}).subscribe(
response => {
console.log("data :"+response);
console.log("data stringify:"+JSON.stringify(response));
});
}
}
Solution 2 using a parent child component
Second.component.ts
import { Component } from '#angular/core';
#Component({
selector: 'app-first-component',
template: `<p>{{data}}</p>`
})
export class SecondComponent{
data:any={};
ngOnInit(){this.getData();}
function getData():any{
this.http.get('/name',{responseType:"json"}).subscribe(
response => {
console.log("data :"+response);
console.log("data stringify:"+JSON.stringify(response));
this.data=data
});
}
}
parent.component.ts
import { Component } from '#angular/core';
import { SecondComponent } from './second.component';
#Component({
selector: 'app-first-component',
template: `
<h3>Get data (via local variable)</h3>
<button (click)="second.getData()">GetData</button>
<app-first-component #second></app-first-component>
`
})
export class FirstComponent{ }
Use Input & Output Decorators
Basic concept ---> DEMO
app.component.html:
<app-component1 (elm)="catch1Data($event)">
</app-component1>
<app-component2 [elm]="datatocomp2" *ngIf="datatocomp2"></app-component2>
parent component : {{datatocomp2 | json}}
app.component.ts:
datatocomp2: any;
catch1Data(data) {
console.log(data)
this.datatocomp2 = data;
}
component1.ts:
#Output () elm : EventEmitter<any> = new EventEmitter<any>();
objectData: any;
constructor() { }
ngOnInit() {
let objectData = {
comp: 'component 1',
data: 'anything'
}
this.objectData = objectData;
this.elm.emit(objectData)
}
component2.ts:
#Input() elm: any;
constructor() { }
ngOnInit() {
console.log(this.elm);
}
You can create store service for your 'global' data:
store.service.ts
import { Injectable } from '#angular/core';
#Injectable()
export class StoreService {
protected store: Map<string, any> = new Map();
constructor() { }
public get(key: string): any {
return this.store.get(key);
}
public set(key: string, value: any) {
this.store.set(key, value);
}
}
And then in yours component (lets call it X) you save data to store:
x.component.ts
import { Component, OnInit } from '#angular/core';
import { HttpClinet } from '#angular/common/http';
import { StoreService } from './store-service.service.ts';
#Component({
selector: 'app-x',
templateUrl: './x.component.html',
styleUrls: ['./x.component.css']
})
export class XComponent implements OnInit {
constructor(
private store: StoreService,
private http: HttpClient,
) { }
ngOnInit() {
}
getResource() {
this.http.get('/name',{responseType:"json"}).subscribe(
response => {
this.store.set('response', response);
console.log("data :"+response);
console.log("data stringify:"+JSON.stringify(response));
});
}
And then in yours other component (lets call it Y) you get your data:
y.component.ts
import { Component, OnInit } from '#angular/core';
import { StoreService } from './store-service.service.ts';
#Component({
selector: 'app-y',
templateUrl: './y.component.html',
styleUrls: ['./y.component.css']
})
export class YComponent implements OnInit {
constructor(
private store: StoreService
) { }
ngOnInit() {
}
getSavedResponse() {
// ask store for the resource
return this.store.get('response');
});
}
This is just simple example, if you know the structure of your response got by http call it would be good idea to make model of it.
Using the store any component can get or set store data.
If you need something more complex look for: #ngrx/store
Cases when you would not need store service:
If you do that http call in parent component then you can use child inputs to pass the data.
If you make that call in child component then use #Output and EventEmitter, to pass up the data (just one level, you can not do this to pass to grandparent)
Regards.

Angular 2/4: Can't update child component view

I want to update the value of str which I used in the view of child component from the parent component, by calling the function change() of child component
here is my code.
import { Component } from '#angular/core';
import { ChildComponent } from './child/child.component';
#Component({
selector: 'app-root',
template: `
<h1>parent</h1>
<button (click)="changeChild()">change child</button>
<app-child></app-child>
`,
styleUrls: ['./app.component.css']
})
export class AppComponent {
constructor(private cc:ChildComponent) {}
changeChild(){
this.cc.change();
}
}
and the child component is:
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-child',
template: `
<p>{{str}}</p>
`,
styleUrls: ['./child.component.css']
})
export class ChildComponent implements OnInit {
private str;
constructor() {
this.str = 'initial'
}
change(){
this.str = 'changed'
}
ngOnInit() {
}
}
but the value str in the view never updated to "changed".
please Any help because I am new in Angular 4 ??
Use #ViewChild to get reference of child component
in Parent component
import {ChildComponent} from 'child.component.ts'
export class ParentComponent{
------
#ViewChild(ChildComponent)
private child:ChildComponent
---
changeChild(){
this.child.change()
}
}