I have a few components; I'm using Injector in constructor for encapsulation
import { Component, Injector, OnInit } from '#angular/core';
#Component({
selector: 'app-base',
templateUrl: './base.component.html',
styleUrls: ['./base.component.css'],
})
export class BaseComponent implements OnInit {
some = '';
constructor(injector: Injector) {
this.some = injector.get(this.some);
}
ngOnInit(): void {}
}
I'm using BaseComponent in other Component
import { BaseComponent } from '../base/base.component';
#Component({
selector: 'app-base-state',
templateUrl: './base-state.component.html',
styleUrls: ['./base-state.component.css'],
})
export class BaseStateComponent extends BaseComponent implements OnInit {
constructor(injector: Injector) {
super(injector);
}
ngOnInit(): void {}
}
BaseStateComponent I'm going to use in others component; Question is:
Is there any way, to make injector in BaseComponent or BaseSateComponent Optional;
I have a case, when I need a component, but I don't need an injector of it;
I know about feature
constructor(#Optional(), #Self() etc...);
But truly to say, I can't understand how it work's; I will be grateful if somebody can explain it Decorators;
stack
The problem is that you want to use #Optional #Self to make injector optional. But, #optional #self works well with something that is injectable which you can provide in providers array at some level. In Angular, services are injectable. So you can use #Optional #Self #SkipSelf with services or something injectable
When you use constrocutor(private someService:SomeService), Angular will check whether SomeService is provided at componoent level means in #component's providers, if not, is it provided at Module Level, if not , is it provided at Root level? This way angular checks entire Injector tree.
When you use #Optional, it will not perform this check and so on....
Keep in mind that Injector resolves a token into a dependency.
Answer to your question
You can use optional parameter approach in typescript by simply providing ? mark next to parameter as shown below,
export class BaseComponent implements OnInit {
some = '';
constructor(private injector?: Injector) { // ? to make parameter optional
this.some = injector.get(this.some);
}
ngOnInit(): void {}
}
it also makes sense because you are using OOPs over classes.
Forked Stackblitz (Fixed some other issues also)
Related
In one of my scenario I have moved duplicate Angular component code to one Utility class and I am re-using the logic by calling the method in all the components.
In that Utility file logic I am using ViewContainerRef and I am executing javscript code at runtime.
Like below I have multiple components and re-using logic from Utility class in all the components.
Here I am getting below error. After some research I came know that ViewContainerRef can be used only in Component or as a Directive.
Could some one please help how do I instantiate ViewContainerRef object in Utility class.
Error Message.
Error: Uncaught (in promise): Error: StaticInjectorError(AppModule)[NgIf -> ViewContainerRef]:
StaticInjectorError(Platform: core)[NgIf -> ViewContainerRef]:
NullInjectorError: No provider for ViewContainerRef!
Component class
import {ViewContainerRef} from '#angular/core';
#Component({
selector: 'dz-page-1',
templateUrl: './component-page-1.component.html',
styleUrls: ['./component-page-1.component.scss']
})
export class ContentInspectPageComponent{
constructor(protected viewContainerRef: ViewContainerRef,){
handleMenuAction(event){
let customUtilHandler = new CustomUtilHandler(this.viewContainerRef);
return customUtilHandler.handleCustomForm();
}
}
}
Utility Class
#Injectable({
providedIn: 'root'
})
export class CustomUtilHandler {
constructor( protected viewContainerRef: ViewContainerRef){
}
handleCustomForm(){
this.viewContainerRef.clear();
const component = this.viewContainerRef.createComponent("customComponent");
component.instance["data"] = inputData;
}
}
Thank you in Advance.
Is there a way to call a child class method from an abstract service? When I do this, the if statement doesn't execute because onInit doesn't exist. I am not sure why it doesn't exist though. Maybe there is an "angular" way of doing this instead of triggering this on the init. Or maybe I just need to call the onInit manually in the component instead. Basically what I am doing is trying to get the initial application data from the server.
#Injectable({providedIn:'root'})
export class RootService {
public constructor(httpClient: HttpClient) {
if(typeof this.onInit === 'function') {
this.onInit()
}
}
}
#Injectable({providedIn:'root'})
export class MyService extends RootService {
public onInit() {
// Do stuff
}
}
#Component()
export MyComponent {
public constructor(myService: MyService) {}
}
Services do not have lifecycle events. However, components have lifecycle hooks such as:
ngOnChanges()
ngOnInit()
ngDoCheck()
...
So you can load data when your component is initialized. If yes, then just use ngOnInit:
import { Component, OnInit } from '#angular/core';
#Component()
export MyComponent implements OnInit {
yourData;
public constructor(myService: MyService) {}
ngOnInit(){
myService.loadData()
.subscribe(s=> yourData = s);
}
}
According to Angular docs:
OnInit is a lifecycle hook that is called after Angular has
initialized all data-bound properties of a directive.
If you add the service to the providers of your component, it will be in your components scope then you can call onInit in your service as well.
But the downside of this is you can no longer share the same instance of the service among your components.
This will be valid if your service only serves one component.
I am trying to write a service in which some events are triggered via respective methods in it so that any component in my application can listen to it.
So, I created a custom event in a service as follows
export class EventsService {
#Output() expandNav: EventEmitter<Number> = new EventEmitter();
trigExpandNav() {
this.expandNav.emit(1);
}
constructor() { }
}
Then I included this service as a provider in my component in which I wanted to emit this event via the trigExpandNav method as follows :
import {
Component,
OnInit,
Output,
EventEmitter
} from '#angular/core';
import {
EventsService
} from '../../services/events.service';
#Component({
selector: 'app-header',
templateUrl: './header.component.html',
styleUrls: ['./header.component.css'],
providers: [EventsService]
})
export class HeaderComponent implements OnInit {
eventsService: EventsService;
fun() {
this.eventsService.trigExpandNav();
}
constructor() {}
ngOnInit() {}
}
But I am getting this error
ERROR in src/app/layout/header/header.component.ts(21,7): error TS2349: Cannot invoke an expression whose type lacks a call signature. Type 'EventEmitter<Number>' has no compatible call signatures.
I don't know where I am doing it wrong, so please help and Thanks in advance.
IMPORTANT
What I am trying to achieve is :
Suppose I have 2 components. I want to trigger an event from 1st component when a button or link is clicked and listen that event in 2nd component from the html file like this sda
For that I made a service in which I am emitting the event through a method. The thing is it's working as expected.
You shouldn't be using event emitters for Service's this is a better place for a RxJS BehaviorSubject or Subject. depending on your need.
#Ouput()s and EventEmitters are for Components only
So your service should become something along the lines of:
export class EventsService {
private expandNavSubject = new BehaviorSubject<number>(null);
trigExpandNav() {
this.expandNavSubject.next(1);
}
get expandNav$(){
this.expandNavSubject.asObservable();
}
constructor() { }
}
}
then in your header component inject the service via the constructor with:
constructor(private eventsService: EventsService){}
and call the trigger expand nav function with this.eventsService.trigExpandNav(); in your fun() function
I am having trouble trying to get the queryparams into a component. For now, I just want to console.log(...) it.
I am using the ActivatedRoute from #angular/router for this task.
I am redeveloping a certain platform for work so unfortunately some irrelevant code will have be to substituted with "..."
My Component.ts code:
import { Component, OnInit, ViewEncapsulation } from '#angular/core';
import { RelevantReportService } from './../reportServices/relevantReports.service';
import { ActivatedRoute ,Params, Router } from '#angular/router';
#Component({
selector: 'vr-reports',
templateUrl: './reports.component.html',
styleUrls: ['./reports.component.scss'],
providers: [RelevantReportService],
encapsulation: ViewEncapsulation.None
})
export class ReportsComponent implements OnInit {
reportSections: any;
constructor( private relevantReportService: RelevantReportService,
private router: Router,
private activatedRoute : ActivatedRoute
) { }
ngOnInit() {
...
console.log(this.activatedRoute.queryParams.value.reportName)
// console.log(this.activatedRoute.queryParams._value.reportName)
}
...
}
When I do console.log(this.activatedRoute.queryParams.value.reportName), the console spits out the queryparams (which is exactly what I wanted) HOWEVER it also says
"Property 'value' does not exist on type 'Observable' "
so I believe this not the correct way of tackling it.
It's observable in order to be able to monitor for changes in the params (by subscribing to observable). To get currently passed query params use:
this.activatedRoute.snapshot.queryParams
You could also use ActivatedRouteSnapshot instead of ActivatedRoute
Nothing surprising there!
activatedRoute.queryParams is an observable, and therefore you need to subscribe to it as per https://angular.io/api/router/ActivatedRoute#queryParams
You need to do the following :
ngOnInit() {
this.activatedRoute.queryParams.subscribe(values => {
console.log(values);//Which will print the properties you have passed
});
}
For Angular5 i would say the best option is using URL tree.
Since a router state is a tree, and the URL is nothing but a serialized state, the URL is a serialized tree. UrlTree is a data structure that provides a lot of affordances in dealing with URLs
Details
https://angular.io/api/router/UrlTree
I want to know how injected's services works on IONIC 2. Specifically, I want to know how many instances will be exists if one service is used in two or more controllers.
In the past, i asked to a collegue, and he tell me that IONIC 2 works with singleton pattern, but in my test, i think it does not.
I have two controllers, A and B; and one service SVC1. Is something like that.
Controller A
import { NavController, Platform } from 'ionic-angular';
import { PageB } from '../pageb/pageB';
import { SVC1 } from '../../providers/svc1';
#Component({
selector: 'page-a',
templateUrl: 'a.html',
providers: [SVC1]
})
export class PageA {
constructor(public navCtrl: NavController, platform: Platform, public svc: SVC1) {
}
onAddEventClicked(event): void {
this.navCtrl.push(PageB);
}
}
Controller B
import { NavController, Platform } from 'ionic-angular';
import { SVC1 } from '../../providers/svc1';
#Component({
selector: 'page-b',
templateUrl: 'b.html',
providers: [SVC1]
})
export class PageB {
constructor(public navCtrl: NavController, platform: Platform, public svc: SVC1) {
}
}
Service
import { Injectable } from '#angular/core';
import { Http } from '#angular/http';
#Injectable()
export class SVC1 {
constructor(public http: Http) {
console.log('creating another instance');
}
}
This is very very simple. PageA has a button. When click it, it go to PageB. PageA and PageB use the same service.
If this service were singleton, the message "creating another instance" will be appears only once. But it's appearing two times.
Why occur this? IONIC use singleton pattern to inyect service reference? Is there a way to have onlye one instance of my service?
Thanks a lot
PS: Sorry for my bad english, hopefully I will improve with more time.
You have set your service as provider for each individual page.This is not the singleton pattern. This is used when you need the service available for only that component.
For singleton pattern,you set it as provider in NgModule in app.module.ts.
#NgModule({
declarations:[..]
imports:[..],
bootstrap:[IonicApp],
entryComponents:[...],
providers:[SVC1] //here
})
export class AppModule { }
and then add the service in the constructor of any components you need.
here
Angular Official docs has information regarding the dependency injection