Cyclic dependency issue in Angular2 - javascript

I developed a custom error handler which implements Angular2 Error Handler class. My custom error handler uses a logger service to log errors. The code looks like as follows:
export class CustomErrorHandler implements ErrorHandler {
constructor(private logger: LoggerService) {}
handleError(error: any): void {
logger.error('....');
}
}
However, since the logger service uses Angular2 router, I cannot inject the logger service to the custom error handler! Running the above code throws the following exception!
Error: Provider parse errors:↵Cannot instantiate cyclic dependency!

You need to inject manually, to avoid the cyclic dependency problem because this class is created before the providers, your code should be:
import { Injectable, Injector } from '#angular/core';
import { Logger } from '...';
#Injectable()
export class CustomErrorHandler implements ErrorHandler {
constructor(private injector: Injector) {}
handleError(error: any): void {
const logger = this.injector.get(Logger);
logger.error('....');
}
}

Related

What causes the "Cannot find name" error in this Angular 11 application?

I am working on an Angular 11 application.
In the service UserService I have:
import { Injectable, OnDestroy } from '#angular/core';
import { UserModel } from '../path/to/UserModel';
export class UserService implements OnDestroy {
public isActiveUser: boolean = false;
public checkUserStatus(user: UserModel) {
return this.isActiveUser;
}
}
I use the above service in a component, like this:
import { UserService } from '../path/to/user-service.service';
export class UserComponent implements OnInit {
public isActiveUser: boolean;
public checkUserStatus() {
this.isActiveUser = this.UserService.checkUserStatus(user);
}
}
The problem
In the above CompositionEvent, on the line this.isActiveUser = this.UserService.checkUserStatus(user) I get the error:
Cannot find name 'user'
What causes this error?
The user variable is missing in your code.
Here are scenarios to set the user variable
import { UserService } from '../path/to/user-service.service';
// Import your model with below path
import { UserModel } from '../path/to/UserModel';
export class UserComponent implements OnInit {
public isActiveUser: boolean;
// Declare your user variable
user: UserModel;
public checkUserStatus() {
this.isActiveUser = this.UserService.checkUserStatus(user);
}
}
Resolve your error :)
In the above CompositionEvent, on the line this.isActiveUser = this.UserService.checkUserStatus(user) I get the error:
Coz no user constant / variable is declared.
Because your user variable is never defined anywhere?

Adding a new service to Angular project

I've added a new Service to an existing Angular project with:
$ ng generate service utils/new
Now I tried to move some methods from AppService to NewService.
Both services have the same constructor:
#Injectable({
providedIn: 'root'
})
export class AppService {
constructor(private http: HttpClient) {}
And
#Injectable({
providedIn: 'root'
})
export class NewService {
constructor(private http: HttpClient) { }
Now in a Component I try to use NewService instead of AppService (I simply replace AppService with NewService).
#Component({
//...
})
export class MyComponent implements OnInit {
constructor(private newService: NewService) {
newService.doSomething(...);
}
ngOnInit(): void {
}
It compiles. But I get a runtime error: ERROR TypeError: n.appService is undefined
I could not understand what the debugger was saying so I made a guess: I added private appService: AppService to the constructor, although it is not being used at all in the code of MyComponent. So now I have this:
#Component({
//...
})
export class MyComponent implements OnInit {
constructor(private appService: AppService, private newService: NewService) {
newService.doSomething(...);
}
ngOnInit(): void {
}
Now it compiles, and I also don't get any runtime error.
This looks strange and counterintuitive to me. What did I miss here?
Do I need some configuration setting to declare the existence of NewService?
This happened because in the html view of MyComponent was a reference to an AppService method. Strangely, the project still compiled and only failed at runtime.
Usually when I refer in html views to entities that are not recognized, I get a compilation error.

NestJS - avoid setContext() in constructor on logger injection

In NestJS I have custom logger:
import { Injectable, Logger, Scope } from '#nestjs/common';
#Injectable({ scope: Scope.TRANSIENT })
export class LoggerService extends Logger {
log(message: any) {
super.log(message);
}
error(message: any) {
super.log(message);
}
warn(message: any) {
super.log(message);
}
debug(message: any) {
super.log(message);
}
verbose(message: any) {
super.log(message);
}
setContext(context: string) {
super.context = context;
}
}
It is registered globally:
import { Global, Module } from '#nestjs/common';
import { LoggerService } from './logger.service';
#Global()
#Module({
providers: [LoggerService],
exports: [LoggerService],
})
export class LoggerModule {}
Is there any way to somehow pass context on injection in service constructor and avoid execution of logger.setContext(context) in every service - instead just set it in LoggerService constructor?
Example usage now:
constructor(private logger: LoggerService) {
this.logger.setContext(ClassName.name);
}
Expected usage:
constructor(private logger: LoggerService) {}
When you do private logger: LoggerService, there's no chance to make that .setContext(Service.name) call.
What you could do is something like:
#Logger(Service.name) private logger: LoggerService
How? Read this article: Advanced NestJS: Dynamic Providers

Run program on init

I would create a program (script) that launches actions when it's get run, so I'm not using routes in this program
I'm using NestJS framework (requirement).
Actually I'm trying to write my code in main.ts file and importing a service with my methods .
import { NestFactory } from '#nestjs/core';
import { AppModule } from './app.module';
import {AppService} from './app.service'
import { TreeChildren } from 'typeorm';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
}
let appService: AppService; <- can't use appService methods
this.appService.
bootstrap();
My service
#Injectable()
export class AppService {
constructor(
#InjectRepository(File) private readonly fileRepository: Repository<File>,
) {}
async getTypes(): Promise<File[]> {
return await this.fileRepository.find();
}
}
I would use services to treat my operations so I sould use DI, which is not working in a non class file.
I would know how to run my operations in init time in a proper way
There are two ways to do this:
A) Lifecycle Event
Use a Lifecycle Event (similar to change detection hooks in Angular) to run code and inject the services needed for it, e.g.:
Service
export class AppService implements OnModuleInit {
onModuleInit() {
console.log(`Initialization...`);
this.doStuff();
}
}
Module
export class ApplicationModule implements OnModuleInit {
constructor(private appService: AppService) {
}
onModuleInit() {
console.log(`Initialization...`);
this.appService.doStuff();
}
}
B) Execution Context
Use the Execution Context to access any service in your main.ts:
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
const appService = app.get(AppService);
}

Application Insights does not track unhandled browser exceptions in Angular app

I'm experiencing an issue with the Microsoft Application Insights SDK for JavaScript that was closed/fixed awhile ago: https://github.com/Microsoft/ApplicationInsights-JS/issues/282
I created a brand new Angular app using the Angular CLI. Then I made these changes, following this article.
Added a monitoring service:
import {Injectable} from '#angular/core';
import {AppInsights} from 'applicationinsights-js';
#Injectable()
export class MonitoringService {
private config: Microsoft.ApplicationInsights.IConfig = {
instrumentationKey: 'KEY_GOES_HERE',
enableDebug: true,
verboseLogging: true
};
constructor() {
if (!AppInsights.config) {
AppInsights.downloadAndSetup(this.config);
}
}
logPageView(name?: string, url?: string, properties?: any, measurements?: any, duration?: number) {
AppInsights.trackPageView(name, url, properties, measurements, duration);
}
logEvent(name: string, properties?: any, measurements?: any) {
AppInsights.trackEvent(name, properties, measurements);
}
trackException(exception: Error) {
AppInsights.trackException(exception);
}
}
Added it to my app.component.ts:
import {Component, OnInit} from '#angular/core';
import {MonitoringService} from './monitoring.service';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
providers: [MonitoringService]
})
export class AppComponent implements OnInit {
title = 'app works!';
constructor(private monitoringService: MonitoringService) { }
ngOnInit() {
this.monitoringService.logPageView();
}
throwAnException() {
this.monitoringService.trackException(new Error('manually track exception'));
throw 'this should appear in app insights'; // but it doesn't
}
}
Made a simple button for throwing the exception in my app.component.html:
<h1>
{{title}}
</h1>
<div (click)="throwAnException()">Click to throw an exception</div>
Logging a page view works, as does tracking the exception by explicitly calling trackException. From reading the documentation and various articles, I was under the impression that uncaught exceptions would always automatically get sent to Application Insights. However, I am not seeing any of those show up in the portal.
What could I be missing here?
Using these versions:
applicationinsights-js: 1.0.11
#types/applicationinsights-js: 1.0.4
I've struggles with the same thing and here is the things you need to know to hack it through:
What is happenning?
Angular catches all the exceptions (swallows them!) and just logs them inside console. I have not seen this behavior being explicitly told in any documentation, but I've tested this in code, so trust me. On the other hand only uncaught exceptions are autocollected! (see here). For collecting caught exceptions ( as is mostly the case when using angular framework) you have to call trackException() explicitly in your code.
How to solve it :
We will implement a service (called MonitoringService in code below) to communicate with azure application insights. Then we will tell angular to use this service to log exceptions in azure ai, instead of logging just into browser console, by extending ErrorHandler class.
1) implement MonitoringService:
We'll be using a service named MonitoringService to communicate with azure application insights. Implement that service like this:
import { Injectable } from "#angular/core";
import { ApplicationInsights } from "#microsoft/applicationinsights-web";
import { environment } from "#env/environment";
#Injectable({
providedIn: "root",
})
export class MonitoringService {
private appInsights: ApplicationInsights;
constructor() {}
startMonitoring(): void {
this.appInsights = new ApplicationInsights({
config: {
instrumentationKey: environment.appInsights.instrumentationKey,
},
});
this.appInsights.loadAppInsights();
this.appInsights.trackPageView();
}
logException(exception: Error, severityLevel?: number) {
this.appInsights.trackException({
exception: exception,
severityLevel: severityLevel,
});
}
}
startMonitoring() should be called on app start up.
2) start monitoring on app start up:
Angular projects mostly have a app.component.ts file which belongs to the root module and is bootstrapped/initialized as the first component. By the term "on app start up", I actually mean the time this component is being initialized.
We'll create an instance of MonitoringService and have it start its job:
import { Component } from '#angular/core';
import { MonitoringService } from 'services/monitoring.service';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
})
export class AppComponent {
constructor(
private monitoringService: MonitoringService
) {
this.monitoringService.startMonitoring();
}
}
3) Log errors into application insights, before they are swallowed by framework:
Extend ErrorHandler class in your project. This class is actually a hook for centralized exception handling in angular spa. Use this hook, to log exceptions before they are swallowed by framework:
import { Injectable, ErrorHandler } from '#angular/core';
import { MonitoringService } from './monitoring.service';
#Injectable({
providedIn: 'root'
})
export class GlobalErrorHandlerService implements ErrorHandler {
constructor(private monitoringService: MonitoringService) { }
handleError(error: any): void {
console.error(error);
this.monitoringService.logException(error);
}
}
4) Register the ErrorHandler with Angular:
In the AppModule make sure to register this Handler with Angular:
#NgModule({
providers: [{provide: ErrorHandler, useClass: GlobalErrorHandlerService}]
})
class AppModule {}
I don't think AppInsights has any knowledge of Angular and the other way around, Angular doesn't know about app insights so you'll probably have to add this in by yourself using a custom ErrorHandler. Have a look at the ErrorHandler official documentation. If you put your this.monitoringService.trackException call in the handleError there it should work fine.

Categories