angular form.io form-builder custom component get reference - javascript

I'm developing in ANGULAR 10
I use form.io form-builder to render components
I add custom component that drow grid (ag-grid) According to this link add custom Components with Angular Elements
every think work well
I drag and drop the table several times to draw multiple tables.
I enclose a photo of how the form looks like after the drag and drop action into the form
The problem .
when form.io render my custom component that draw the ag-grid , i need to get the connection string and the sql statement from the component definition (json).
I do not know how to get this information from my custom component at the time it's be rendering. Without this information, I do not know generate the column names and row's content.
This is my project
builder component contain the formio tag
aggrid component is my custom component for display ag grid
formio.ts
import { Injector } from '#angular/core';
import { FormioCustomComponentInfo, registerCustomFormioComponent } from 'angular-formio';
import { AggridWrapperComponent } from './aggrid-wrapper.component';
export function minimalEditForm() {
return {
components: [
{ key: 'type', type: 'hidden' },
{
weight: 10,
type: 'textarea',
input: true,
key: 'key',
label: 'sql statement',
tooltip: 'please enter your sql statement',
}
],
};
}
const COMPONENT_OPTIONS: FormioCustomComponentInfo = {
type: 'sqlaggrid',
selector: 'sql-grid',
editForm: minimalEditForm,
title: 'sql-grid',
group: 'basic',
icon: 'fa fa-star',
};
export function registerAgGridComponent(injector: Injector) {
registerCustomFormioComponent(COMPONENT_OPTIONS, AggridWrapperComponent, injector);
}
aggrid-wrapper.component.html
<ag-grid-angular style="width: 500px; height: 500px;" class="ag-theme-alpine"
[gridOptions]="gridOptions">
</ag-grid-angular>
AggridWrapperComponent
import { Component, EventEmitter, Input, ElementRef, Output ,ViewChild} from '#angular/core';
import { FormioCustomComponent } from 'angular-formio';
import { Grid, GridOptions } from "ag-grid";
#Component({
selector: 'app-aggrid-wrapper',
templateUrl: './aggrid-wrapper.component.html',
styleUrls: ['./aggrid-wrapper.component.css']
})
export class AggridWrapperComponent implements FormioCustomComponent<number> {
#Input()
value: number; //number is missing (null)
#ViewChild('aggrid') input;
#Output()
valueChange = new EventEmitter<number>();
#Input()
disabled: boolean;
private _value: number;
public gridOptions: GridOptions;
constructor(private elRef: ElementRef) {
this.gridOptions = <GridOptions>{
columnDefs: this.createColumnsDefs(),
onGridReady: (params) => {
this.gridOptions.api.setRowData(this.executeStatement());
}
}
}
createColumnsDefs() {
/* return the grid columns */
/*If I could get the field definition containing the SQL statement then I could return the columns of the grid */
return ???;
}
executeStatement(){
/* get the grid rows */
/*If I could get the field definition containing the SQL statement then I could execute the statement and back the rows */
return ??? */
}
}
app.module.ts
import { BrowserModule } from '#angular/platform-browser';
import { NgModule ,Injector } from '#angular/core';
import { RouterModule } from '#angular/router';
import {AppConfig} from './formio-config';
//import { AppRoutingModule } from './app-routing.module'
import { FormioModule } from 'angular-formio';
import { AppComponent } from './app.component';
import { BuilderComponent } from './builder/builder.component';
import { FormsModule } from '#angular/forms';
import { BrowserAnimationsModule } from '#angular/platform-browser/animations';
import { NgbModule } from '#ng-bootstrap/ng-bootstrap';
import { RatingWrapperComponent } from './rating-wrapper/rating-wrapper.component';
import { registerAgGridComponent} from './aggrid-wrapper/formio'
import { AggridWrapperComponent } from './aggrid-wrapper/aggrid-wrapper.component';
import { AgGridModule } from 'ag-grid-angular';
import { HttpClientModule } from '#angular/common/http';
#NgModule({
declarations: [
AppComponent,
BuilderComponent,
AggridWrapperComponent
],
imports: [
BrowserModule,
HttpClientModule,
FormsModule,
FormioModule,
BrowserAnimationsModule,
NgbModule,
AgGridModule.withComponents([])
],
exports: [RouterModule],
providers: [ ],
bootstrap: [AppComponent]
})
export class AppModule{
constructor(injector: Injector) {
registerAgGridComponent(injector)
}
}
I am missing the reference to the component definition
any idea ?

have you tried getting the object by its key?
component = form.getComponent('keyName')

The problem is:
from Where did the form object come from.
I drag and drop the same component several times, and I do not know within the component (AggridWrapperComponent) which instance I am.

Hey I was able to achieve by creating a Subject and pushing the data at the OnChange of App Component and then retrieving where ever I want to.

Related

Dynamically loaded Angular 10 component cannot access CommonModule

I'm currently working on an Angular application. In one of my methods, I dynamically create a component, however I am unable to use ngClass, ngIf and other such directives from the CommonModule in the component.
Below is an example of the error:
Error when I use ngIf or ngClass inside the dynamically loaded logo component
WHAT I DID ALREADY:
I've imported commonModule in my project app.module.ts
I've imported commonModule in my display component and it works as I'm able to use ngIf and ngClass in every other component without any problem
Also I'm able to import any component without errors as long as I'm not using any directive from the CommonModule in my html
I've tried importing an instance of the NgModule through component factory createComponent function as shown in the angular documentation:
Quick view of angular documentation on component factory createcomponent
I have spent hours on this and believe it's related to my use of the createComponent method.
Please Help!
Here's my app.module.ts
import { NgModule } from '#angular/core';
import { BrowserModule, HammerGestureConfig, HammerModule,
HAMMER_GESTURE_CONFIG } from '#angular/platform-browser';
import { GoogleAnalyticsService } from
'./shared/services/googleanalytics'; // import our Google Analytics
service
import { BrowserAnimationsModule } from '#angular/platform-
browser/animations';
import { HttpClientModule } from '#angular/common/http';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { CommonModule } from '#angular/common';
#NgModule({
declarations: [AppComponent],
imports: [
CommonModule,
BrowserModule,
AppRoutingModule,
HttpClientModule,
BrowserAnimationsModule,
],
providers: [GoogleAnalyticsService],
bootstrap: [AppComponent],
})
export class AppModule {}
Here's my Display component (Bdoc-a.component.ts)
import {
Component, OnInit, Input, Output, Renderer2, AfterViewInit,
AfterContentChecked, ViewChild, ElementRef,
ComponentFactoryResolver, ViewContainerRef, ViewChildren, QueryList
} from '#angular/core';
import { StorageService } from
'../../../../shared/services/storage.service';
import { AuthService } from '../../../../auth/auth.service';
#Component({
selector: 'app-bdoc-a',
templateUrl: './bdoc-a.component.html',
styleUrls: ['./bdoc-a.component.scss'],
})
export class BdocAComponent implements OnInit, AfterViewInit,
AfterContentChecked {
#Input() docConfig = { aspectRatio: '4:3', width: 800, height: 500 };
#Input() bgStyles = { shadow: true, bgClr: '#ffcc00' };
#Input() showBtns = false;
fWidth = this.docConfig.width;
fMaxWidth = this.docConfig.width;
fHeight = this.docConfig.height;
fMaxHeight = this.docConfig.height;
#Input() url = '';
#Input() settings = { ... };
...
#ViewChildren('loadDynAssetElEditItms', { read: ViewContainerRef })
biEditEls: QueryList<ViewContainerRef>;
#ViewChildren('loadDynAssetElViewItms', { read: ViewContainerRef })
biViewEls: QueryList<ViewContainerRef>;
loadDynAssetEl: any;
#ViewChild('bieditorFloat', { read: ElementRef }) bieditorFloat:
ElementRef;
#ViewChild('bieditorFloatViewer', { read: ElementRef })
bieditorFloatViewer: ElementRef;
// variable to hold all document page elements
allDocPages: any;
allDocPageComp = [];
constructor(private storage: StorageService,
private resolver: ComponentFactoryResolver,
private authService: AuthService,
private elmRef: ElementRef) {
this.url = 'templates/logo/1/logo1a/logo1a.component';
}
ngAfterViewInit(): void {
this.initBrandAsset();
}
initBrandAsset(): void {
this.allDocPageComp = [];
setTimeout(() => {
for (let i = 0; i < this.pages; i++) {
this.loadDynAsset(this.url, i);
}
this.setDocPageVar();
}, 200);
}
async loadDynAsset(url, pgIndex) {
const impEl = await import( 'src/app/' + url);
const allKeys = Object.keys(impEl);
this.biEditEls.forEach((itm, i) => {
if (i === pgIndex) {
itm.clear();
const newComp = itm.createComponent(
this.resolver.resolveComponentFactory(impEl[allKeys[0]]));
newComp.instance['bol']['test'] = 'LOGO TEST TEXT HERE... ' + pgIndex;
this.allDocPageComp.push(newComp.instance);
}
});
}
}
Here's my Logo1a.component.html
<div class="baItem biLogo logo1a edit">
<div class="null bilNull">
<ng-container>
<div class="bilSymb">
<div class="null">
<div #forTxtLogo class="forTxtLogo" *ngIf="config.symb.mode ===
'txt'">
<div class="symbItm"><div class="nl">B</div></div><div
class="symbItm"><div class="nl">S</div></div>
</div>
<div #forImgSvgLogo class="forImgSvgLogo" *ngIf="config.symb.mode
=== 'svg' || config.symb.mode === 'img'">
<div class="symbItm"><div class="nl"></div></div>
</div>
</div>
</div><!-- end of bilSymb -->
</ng-container>
<ng-container *ngIf="config.body.show">
<div class="bilBody">
<div class="null">
<ng-container *ngIf="config.body.txt.show">
<div #forTxtArea class="forTxtArea">
<div class="null">Logo Body Text Area</div>
</div>
</ng-container>
<ng-container *ngIf="config.body.tag.show">
<div #forTagArea class="forTagArea"><div class="null">Logo Tag
Area...</div></div>
</ng-container>
</div>
</div><!-- end of bilBody -->
</ng-container>
</div><!-- end of biNull -->
</div><!-- end of biLogo-->
Here's my Logo1a.component.ts
import { NgModule, Component, OnInit, Input, Output, Renderer2,
AfterViewInit, AfterContentChecked, ViewChild, ElementRef,
ComponentFactoryResolver, ViewContainerRef, ViewChildren, QueryList
} from '#angular/core';
import { StorageService } from
'../../../../shared/services/storage.service';
import { AuthService } from '../../../../auth/auth.service';
#Component({
selector: "app-logo1a",
templateUrl: "./logo1a.component.html",
styleUrls: ["./logo1a.component.scss"],
})
export class Logo1aComponent implements OnInit, AfterViewInit {
#ViewChild('loadExtra', { read: ViewContainerRef }) loadExtra:
ViewContainerRef;
#Input() bol = { test: 'LOGO 1A LOADED!' }; // breadth of life
public config = {
symb: {
show: true,
mode: 'txt', // 'txt', 'symb', 'img'
},
body: {
show: true,
txt: { show: true },
tag: { show: true }
}
}
constructor(private storage: StorageService,
private resolver: ComponentFactoryResolver,
private vcRef: ViewContainerRef,
private authService: AuthService,
private elmRef: ElementRef) { }
ngOnInit(): void {}
ngAfterViewInit() {}
}
Here's my Logo1a.module.ts
import { NgModule } from '#angular/core';
import { CommonModule } from '#angular/common';
import { Logo1aComponent } from './logo1a.component';
#NgModule({
imports: [CommonModule],
declarations: [Logo1aComponent],
exports: [Logo1aComponent]
})
export class Logo1aModule {}
But this was the error that was thrown after I dynamically imported the module instead of the component in my Angular 10 app as #jburtondev suggested:
ERROR Error: Uncaught (in promise): Error: ASSERTION ERROR: Type passed in is not ComponentType, it does not have 'ɵcmp' property.
....
The component needs to be in its own module which declares the CommonModule. Otherwise, Angular cannot associate it with the CommonModule at runtime.
1. Create a component module
#NgModule({
declarations: [ YourDynamicComponent ],
imports: [ CommonModule ] // THIS IS WHAT WILL TELL ANGULAR TO LOAD IT INTO YOUR COMPONENT
exports: [ YourDynamicComponent ]
})
export class YourDynamicModule { }
2. Load the component
constructor(private compiler: Compiler, private viewContainerRef: ViewContainerRef) {}
createDynamicComponent(): void {
const componentModule = this.compiler.compileModuleAndAllComponentsSync(YourDynamicModule);
const factory = componentModule.componentFactories.find(c => comp.componentType === YourDynamicComponent);
this.viewContainerRef.createComponent(factory);
}
3. It should now be be decorated with the CommonModule
So I finally got a not-so-clean working approach to my question.
I created a new module, LogoModules, where I imported all the logo modules I will be displaying in my display component
import { NgModule } from '#angular/core';
import { Logo1aModule } from '../templates/logo/1/logo1a/logo1a.module';
import { Logo2aModule } from '../templates/logo/2/logo2a/logo2a.module';
import { Logo3aModule } from '../templates/logo/3/logo3a/logo3a.module';
#NgModule({
// imports: [],
// exports: [],
})
export class LogoModules {}
This was modified a bit from #jburtondev's suggestion to pre-import all the modules but I still think there should be a cleaner way to do this part. Dynamically loading the modules and components without pre-importing the modules still works but also fails sometimes. I think this is due to some settings with the compiler and webpack in tsconfig. I need more research to clarify this.
I imported the LogoModules module in my display component
import { LogoModules } from '../templates/logo/logos.module';
...
BUT ...
So I am able to store the relative paths of each logo template in my database and display them dynamically based on user clicks, I updated my dynamic component loader in my Display component (Bdoc-a.component.ts) as shown below:
....
async loadDynAsset(url, pgIndex) {
const mObj = await import('src/app/' + url2);
const mKeys = Object.keys(mObj);
const mName = mKeys[0];
this.compiler.compileModuleAndAllComponentsAsync(mObj[mName])
.then((factories) => {
const f = factories.componentFactories[0];
const newComp = this.testDynComp.createComponent(f);
newComp.instance['bol']['test'] = 'LOGO TEST TEXT HERE... ' + pgIndex;
});
....
}
....
And then I was able to get a consistent result without errors. Thanks again to #jburtondev for some suggestions. I'll update my answer as soon as I discover a better approach to my question.

Multiselect dropdown control - Primeng - Angular 6 - Select all options by default on page load and display them as selected label

I have a priming multi-select drop-down control. On page load, all options should be selected and the selected label should display "All selected". How to fetch the "Select All" checkbox status in priming multi-select control?
When all options are selected, the dropdown displays 3 items selected.
Here instead of 3 items selected, it should display "All". And all the options should be selected by default on page load. Help in resolving this.
AppComponent.html
<div id="statusdrpdown" #statusdropdown>
<p-multiSelect [options]="status" [(ngModel)]="selectedStatus" [maxSelectedLabels]="1" class="multiselectcss" [filter]="false" (onPanelShow) =" funShow()" optionLabel="name"></p-multiSelect>
<label id="statuslbl" class="drpdownlbl"><br />Status</label>
</div>
----------------------------------------------------------------------------
App.Component.ts
import { Component , ViewChild, ElementRef} from '#angular/core';
import { SampleData } from './sample-data';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
#ViewChild("statusdropdown", { read: ElementRef }) tref: ElementRef;
status: SampleData[];
selectedStatus: SampleData[];
constructor() { }
ngOnInit() {
this.status = [
{ name: 'Active', value: '1' },
{ name: 'Inactive', value: '2' },
{ name: 'Status-complete-or-active', value: '3' },
];
}
funShow() {
var h = this.tref.nativeElement;
var text = document.createTextNode("All");
h.childNodes[0].lastChild.lastChild.childNodes[1].childNodes[1].after(text);
}
}
--------------------------------------------------------------------------
App.module.ts
import { NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { FormsModule } from '#angular/forms';
import { AppComponent } from './app.component';
import { HelloComponent } from './hello.component';
import { MultiSelectModule } from 'primeng/multiselect';
import { BrowserAnimationsModule } from '#angular/platform-browser/animations';
#NgModule({
imports: [ BrowserModule, FormsModule, MultiSelectModule, BrowserAnimationsModule],
declarations: [ AppComponent, HelloComponent ],
bootstrap: [ AppComponent ]
})
export class AppModule { }
Primeng version - in package.json
"primeicons": "^1.0.0",
"primeng": "^7.0.0-beta.1"
SampleData interface -
export interface SampleData {
name: string,
value: string
}
Try this when your data is present (Constructor, OnInit ..) :
this.status.map((item) => this.selectedStatus.push(item));
PS: And do not forget to initialize your selectedStatus to empty array so that you can fill it later.
Here is a stackblitz for your case.
You need to set: maxSelectedLabels property to "2" and the selectedItemsLabel to "All selected" in your template markup.

Angular 6 when component is routed to the d3/event-drops data points don't display in Safari

I have created a test app to replicate the issue I am experiencing.
codebase: https://github.com/mohammadfarooqi/event-drops-d3-test-app
sample demo deployed (view in safari to see the issue): https://mohammadfarooqi.github.io/event-drops-d3-test-app/
I am using event-drops timeline to display some 'drops' (points on timeline). I have created the timeline in a component called 'comp-a'. I have also created a component called 'comp-b' that uses 'comp-a'. I also have a button in comp-b that routes the user to 'comp-a'.
The issue that I am having is that, in comp-b the event-drops timeline displays with no issues including the 'drops' (points on timeline). However, when we click on the button to go to 'comp-a' from 'comp-b', the comp-a component renders however, the 'drops' on the timeline do not show in Safari (mobile/tablet) however all works in Chrome.
comp-a.component.html
<div id="eventdrops-demo"></div>
comp-a.component.ts
import { Component, OnInit } from '#angular/core';
import * as d3 from 'd3v4';
import eventDrops from 'event-drops';
#Component({
selector: 'app-comp-a',
templateUrl: './comp-a.component.html',
styleUrls: ['./comp-a.component.css']
})
export class CompAComponent implements OnInit {
constructor() { }
ngOnInit() {
const chart = eventDrops({
d3,
drop: {
date: d => d.date
}
});
const repositoriesData = [
{
name: 'admin-on-rest',
data: [{ date: new Date('2018/01/15 14:21:31') } ],
},
{
name: 'event-drops',
data: [{ date: new Date('2018/01/15 13:24:57') } ],
},
{
name: 'sedy',
data: [{ date: new Date('2018/01/15 13:25:12') } ],
},
];
d3
.select('#eventdrops-demo')
.data([repositoriesData])
.call(chart);
}
}
comp-b.component.html
<p>
comp-b works!
</p>
<app-comp-a></app-comp-a>
<button (click)="goto()">test</button>
comp-b.component.ts
import { Component, OnInit } from '#angular/core';
import { Router } from '#angular/router';
#Component({
selector: 'app-comp-b',
templateUrl: './comp-b.component.html',
styleUrls: ['./comp-b.component.css']
})
export class CompBComponent implements OnInit {
constructor(private router: Router) { }
ngOnInit() {
}
goto() {
this.router.navigate(['a']);
}
}
app-routing.module.ts
import { NgModule } from '#angular/core';
import { CommonModule } from '#angular/common';
import { RouterModule, Routes } from '#angular/router';
import { CompAComponent } from './components/comp-a/comp-a.component';
import { CompBComponent } from './components/comp-b/comp-b.component';
const routes: Routes = [
{ path: '', component: CompBComponent },
{ path: 'a', component: CompAComponent }
];
#NgModule({
imports: [
CommonModule,
RouterModule.forRoot(routes)
],
exports: [ RouterModule ],
declarations: []
})
export class AppRoutingModule { }
Just for future reference for someone else running into this issue. Basically as mentioned the 'dots' were not being shown in safari. The reason for that was that in SPA the paths are generated on the fly (virtual paths). The event-drops lib add's a css style called 'filter': 'url(#metaballs)', and safari did not understand that the path to #metaballs in the svg is actually domain/virtual/route/#metaballs. Therefore the fix was to grab the returned d3 object as shown in comp-a.component.ts and simply over write the filter property for all g.drops with filter should be 'url(' + window.location.href + '#metaballs)'.
ie: d3.select('#eventdrops-demo').selectAll('g.drops').style('filter', 'url(' + window.location.href + '#metaballs)')
Hope this helps.

angular component load multiple time when ngModel is added in template

I'm trying to create angular form.
I have referenced the form in that angular module.
The problem is when i add ngModel in input textbox, the angular component loads multiple times in the page.
Following is the module code
import { NgModule } from '#angular/core';
import { CommonModule } from '#angular/common';
import { Routes, RouterModule } from '#angular/router';
import { HeaderProfileComponent } from './header-profile.component';
import { LayoutModule } from '../../../../layouts/layout.module';
import { DefaultComponent } from '../../default.component';
import { FormsModule } from "#angular/forms";
import { BrowserModule } from '#angular/platform-browser';
import { UserService } from '../../../../../_services/user.service';
const routes: Routes = [
{
"path": "",
"component": DefaultComponent,
"children": [
{
"path": "",
"component": HeaderProfileComponent
}
]
}
];
#NgModule({
imports: [
FormsModule,CommonModule, RouterModule.forChild(routes), LayoutModule
], exports: [
RouterModule
], declarations: [
HeaderProfileComponent
],providers:[
UserService
]
})
export class HeaderProfileModule {
}
following is the component code
import { Component, OnInit, ViewEncapsulation } from '#angular/core';
import { Helpers } from '../../../../../helpers';
import { UserService } from '../../../../../_services/user.service';
import { ApplicationUser } from '../../../../../Entities/UserDetail';
#Component({
selector: ".m-grid__item.m-grid__item--fluid.m-wrapper",
templateUrl: "./header-profile.component.html",
encapsulation: ViewEncapsulation.None,
})
export class HeaderProfileComponent implements OnInit {
appUser: ApplicationUser = null;
newUser: ApplicationUser = null;
constructor(private _userService: UserService) {
this.appUser = new ApplicationUser();
this.newUser = new ApplicationUser();
}
ngOnInit() {
this._userService.getCurrentUser()
.subscribe((data: ApplicationUser) => {
this.appUser = data;
}, (err: Response) => {
});
}
createUser(){
debugger;
}
}
in angular component HTML:
<form (ngSubmit)="createUser()" class="m-form m-form--fit m-form--label-align-right">
<input class="form-control m-input" type="password" [(ngModel)]="newUser.FirstName" >
</form>
Wen i add only without mgNodel, it works fine.
The behavior was due to theme i was using.
Theme handled the exceptions and never throw in console so angular tried to load the component even after exceptions caused this behavior.
Removed the code block and let angular throw in browser shows input should contain name attribute with ngModel , adding attribute -> Fixed

Emit events between nested components grandchild to root component

I have wheels.component nested to car.component.
wheels.component:
export class WheelsComponent {
#Output() onLoaded : EventEmitter<string>() = new EventEmitter<string>();
private downloadAllFiles(url: string) {
this.onLoaded.emit('Hello, World 1!');
//some operations to wait
this.onLoaded.emit('Hello, World 2!');
};
}
Component car.component is not written at html page, but called through routing at car-routing.module.ts:
#NgModule({
imports: [
RouterModule.forChild([
{
path: 'sfactmessage/:id',
component: CarComponent,
resolve: {
card: cardResolver
}
}
])
],
exports: [RouterModule]
})
export class CarRoutingModule {}
What I want is to handle event emitted from wheels.component, not at car.component, but at app.component.
Is it possible to handle event at app.component?
The plunker sample is not working (sorry, this is my first plunkr example), but gives a view how my app is arranged.
Hello_ friend.
So basically if you want to use events globally in your application you can use a Service in combination with EventEmitter
In this case you create a service for example car.service.ts
import { Injectable, EventEmitter } from '#angular/core';
#Injectable()
export class CarService {
onLoaded : EventEmitter<string> = new EventEmitter<string>();
}
Then use this service in a child component to emit events like this wheels.component.ts
import { Component, EventEmitter } from '#angular/core';
import { CarService } from './car.service';
#Component({
selector: 'wheels',
template: '<a (click)="sendValues()"> Click me to send value </a>'
})
export class WheelsComponent {
constructor(private carService:CarService ){}
sendValues() {
/* Use service to emit events that can be used everywhere in the application */
this.carService.onLoaded.emit('Button in WheelsComponent was clicked ...');
};
}
and then capture this event from AppComponent for example app.component.ts
import { Component, OnInit, OnDestroy } from '#angular/core';
import { CarService } from './cars/car.service';
import { Subscription } from 'rxjs';
#Component({
selector: 'my-app',
templateUrl: `src/app.component.html`
})
export class AppComponent implements OnInit, OnDestroy{
private subscription: Subscription;
private loading = true;
name = 'Angular';
constructor(private carService: CarService){}
ngOnInit(){
this.subscription = this.carService.onLoaded.subscribe((message) => {
/*
Here you receive events from anywhere where
carService.onLoaded.emit() is used
**/
alert(`From AppComponent -> ${message}`);
});
}
ngOnDestroy(){
/* Don't forget to unsubscribe when component is destroyed */
this.subscription.unsubscribe();
}
}
I M P O R T A N T______________
If you want your service to work globally you need to declare it in the top level providers for example app.module.ts is a good place:
import { NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { FormsModule } from '#angular/forms';
import { AppComponent } from './app.component';
import { CarComponent} from './cars/car.component';
import { WheelsComponent} from './cars/wheels.component';
import { HomeComponent} from './home.component';
import { routing } from './app.routing';
import { CarService } from './cars/car.service';
#NgModule({
imports: [ BrowserModule, FormsModule, routing ],
declarations: [ AppComponent, CarComponent, WheelsComponent, HomeComponent ],
providers: [ CarService ], // <-------- SEE HERE
bootstrap: [ AppComponent ]
})
export class AppModule { }
CLICK HERE TO SEE THE DEMO

Categories

Resources