This is a glossary of all the common issues in Angular
  • 09-Jan-2023
Lightrun Team
Author Lightrun Team
Share
This is a glossary of all the common issues in Angular

Troubleshooting Common Issues in Angular Angular

Lightrun Team
Lightrun Team
09-Jan-2023

Project Description

Angular is a popular front-end web development framework that was developed and is maintained by Google. It allows developers to build single-page applications (SPAs) using HTML, CSS, and JavaScript. Angular has a number of features that make it an attractive choice for web development, including its use of components, which are reusable building blocks that can be combined to create a functioning application, and its built-in support for dependency injection, routing, and form validation.

One of the key benefits of using Angular is its ability to facilitate the creation of complex, dynamic user interfaces. It does this through the use of a virtual DOM (document object model) and a reactive programming paradigm, which allows for efficient updates to the user interface and smooth, responsive interactions with the application. Angular also has a strong focus on performance, which makes it well-suited for building modern, interactive applications that are optimized for speed and efficiency.

In addition to its core features, Angular also has a robust ecosystem of third-party libraries and tools that can be used to extend its functionality. This includes a range of UI components, utility libraries, and integrations with other frameworks and technologies. These resources, along with an active community of developers and a comprehensive documentation set, make Angular a powerful and flexible choice for front-end web development.

Troubleshooting Angular Angular with the Lightrun Developer Observability Platform

Getting a sense of what’s actually happening inside a live application is a frustrating experience, one that relies mostly on querying and observing whatever logs were written during development.
Lightrun is a Developer Observability Platform, allowing developers to add telemetry to live applications in real-time, on-demand, and right from the IDE.

  • Instantly add logs to, set metrics in, and take snapshots of live applications
  • Insights delivered straight to your IDE or CLI
  • Works where you do: dev, QA, staging, CI/CD, and production

Start for free today

The following issues are the most popular issues regarding this project:

Reactive Form change through ControlValueAccessor doesn’t update view

If you are using a custom ControlValueAccessor to update a reactive form in Angular and are finding that the changes are not being reflected in the view, there are a few potential causes to consider.

First, make sure that you are properly implementing the ControlValueAccessor interface and its required methods (writeValue, registerOnChange, registerOnTouched). These methods are responsible for communicating changes to the form control and the view, so if they are not implemented correctly, the view may not update as expected.

Next, check that you are correctly calling the registered change function (the one passed to registerOnChange) whenever the form control’s value changes. This function is responsible for updating the form model, so if it is not being called, the changes may not be reflected in the form.

It’s also possible that there is an issue with the way the form control is being bound to the template. Make sure that you are using the correct form control directive (such as formControlName or ngModel) and that it is correctly bound to the form control instance.

Finally, if none of these solutions solve the issue, you may want to try debugging the form control’s value and change function to see where the issue is occurring. You can do this by adding console.log statements or by using the browser’s developer tools to inspect the form control’s state.

FormGroup.disable() and FormGroup.enable() do not allow resetting disabled state.

To reset the disabled state of a FormGroup, you can either call the enable() or disable() method on the group again with the desired disabled state, or you can set the disabled property of the group directly.

Here’s an example of how you might reset the disabled state of a FormGroup:

import { FormGroup } from '@angular/forms';

// ...

resetForm(form: FormGroup) {
  form.reset();
  form.enable();
  // or:
  // form.disabled = false;
}
Alternatively, you can use the setDisabled() method to set the disabled state of the group. This method takes a boolean value that indicates whether the group should be disabled or enabled.
import { FormGroup } from '@angular/forms';

// ...

resetForm(form: FormGroup) {
  form.reset();
  form.setDisabled(false);
}

Forms: State that setErrors() will make status === INVALID regardless of value passed for key

The setErrors method of the AbstractControl class in Angular allows you to set the errors object for the control. The errors object is a key-value map, where the keys are error names and the values are the error messages or validation functions that return the error messages. When you call setErrors, you can pass an object containing one or more error keys and their associated messages or validation functions.

Here is an example of how you might use the setErrors method:

this.form.controls['email'].setErrors({
  'incorrect': true
});

This will set the errors object for the email control to { incorrect: true }, which will make the control’s status become INVALID.

If you want to set the errors object and also update the status of the control to VALID, you can use the setErrors method along with the updateValueAndValidity method. Here is an example:

this.form.controls['email'].setErrors(null);
this.form.controls['email'].updateValueAndValidity();

This will set the errors object for the email control to null, which will make the control’s status become VALID.

Form control value is null when initialized with undefined

If you initialize a form control with a value of undefined, the control’s value property will be null when the form is initialized. This is because the value property of a form control is initialized to null if the value passed to the control’s constructor is null or undefined.

Here is an example of how you might initialize a form control with a value of undefined:

this.form = new FormGroup({
  email: new FormControl(undefined)
});

In this example, the value property of the email control will be null when the form is initialized.

If you want to initialize the form control with a non-null value, you can pass the desired value as an argument to the FormControl constructor. For example:

this.form = new FormGroup({
  email: new FormControl('test@example.com')
});

In this example, the value property of the email control will be 'test@example.com' when the form is initialized.

FormGroup reset() doesn’t reset custom form control

The reset() method of the FormGroup class in Angular can be used to reset the form to its initial state. When you call reset(), the form’s controls will be returned to their initial values and their touched and dirty status will be set to false.

However, the reset() method will only reset the form’s built-in controls, such as FormControl and FormArray. If you have created a custom form control that extends one of these built-in controls, the reset() method will not reset your custom control.

To reset a custom form control, you will need to implement a reset method for your control that sets its value and status to their initial states. You can then call this reset method from the form’s reset() method, or you can call it directly whenever you need to reset the control.

Here is an example of how you might implement a reset method for a custom form control:

class MyCustomControl extends FormControl {
  reset() {
    this.setValue(null);
    this.setErrors(null);
    this.markAsPristine();
    this.markAsUntouched();
  }
}

You can then use this custom control in a form like this:

this.form = new FormGroup({
  customControl: new MyCustomControl()
});

this.form.reset();  // This will reset the form and the custom control

HttpClient should allow different responseTypes for success response and error response

The HttpClient in Angular allows you to specify a responseType when making HTTP requests. The responseType specifies the type of data that the server will send back in the response. You can specify the responseType by setting the responseType property of the request options object.

For example, here is how you might make an HTTP request with a responseType of 'json':

this.http.get('/api/data', { responseType: 'json' }).subscribe(
  data => console.log(data),
  error => console.error(error)
);

In this example, the server’s response will be parsed as JSON and the resulting JSON object will be passed to the data callback.

The responseType property applies to both the success response and the error response. If you want to specify different responseType values for the success response and the error response, you will need to make two separate HTTP requests, one for the success response and one for the error response.

Query params always purport to be empty on page load even though provided

If you are using the ActivatedRoute service to get the query parameters from the current route in an Angular application, and you are finding that the query params object appears to be empty when the page is first loaded, there are a few things you can try.

First, make sure that you are subscribing to the queryParams observable of the ActivatedRoute service, rather than trying to access the queryParams object directly. The queryParams observable will emit the latest value of the query params whenever the params change. Here is an example of how you might subscribe to the queryParams observable:

this.route.queryParams.subscribe(params => {
  console.log(params);  // { param1: 'value1', param2: 'value2' }
});

Next, check that the query params are actually being included in the URL when the page is loaded. You can do this by inspecting the URL in the browser’s address bar. If the query params are not being included in the URL, there may be a problem with the way you are generating the URL or with the router configuration.

Finally, make sure that you are using the correct case for the query param keys. The queryParams object is case-sensitive, so if the keys in the URL do not match the keys in the object, the values will not be included in the object.

Checkbox [ngModel] does not reflect model value when updated in (onModelChange)

If you are using the [ngModel] directive to bind a checkbox to a model value in an Angular template, and you are finding that the checkbox does not reflect the model value when it is updated in the (ngModelChange) event handler, there may be a problem with the way you are updating the model value.

The [ngModel] directive creates a two-way data binding between the checkbox and the model value. This means that changes to the model value should be reflected in the checkbox, and changes to the checkbox should be reflected in the model value.

If you are updating the model value in the (ngModelChange) event handler, make sure that you are using the correct syntax to update the value. Here is an example of how you might do this:

<input type="checkbox" [ngModel]="modelValue" (ngModelChange)="modelValue = $event">

In this example, the modelValue variable is bound to the checkbox via the [ngModel] directive, and the (ngModelChange) event handler updates the modelValue variable with the new value of the checkbox.

If you are still having trouble getting the checkbox to reflect the model value, try using the [(ngModel)] syntax to create a two-way data binding. This syntax combines the [ngModel] and (ngModelChange) directives into a single syntax:

<input type="checkbox" [(ngModel)]="modelValue">

Improve error message “No value accessor for form control with unspecified name attribute” to suggest what to do to fix the issue

The error message “No value accessor for form control with unspecified name attribute” in Angular indicates that there is a form control in your template that does not have a name attribute and does not have a value accessor specified.

A value accessor is a directive or component that provides a way for the form control to communicate its value to the form. Some common value accessors include [ngModel], [formControl], and [formControlName].

To fix this error, you will need to add a name attribute to the form control and specify a value accessor for the control. Here is an example of how you might do this:

<input type="text" name="firstName" [(ngModel)]="firstName">

In this example, the form control is an <input> element with a name attribute of “firstName” and a value accessor of [(ngModel)].

If you are using a custom form control that does not have a name attribute, you can specify the name attribute using the @Input() decorator. Here is an example:

@Component({
  selector: 'my-custom-control',
  template: '<input type="text" [(ngModel)]="value">'
})
export class MyCustomControl {
  @Input() name: string;
  @Input() value: any;
}

You can then use this custom control in your template like this:

<my-custom-control name="firstName" [(ngModel)]="firstName"></my-custom-control>

[testing] valueChanges-Event of Control is not executed when dispatching Event for Input-Field

It looks like you are trying to test the valueChanges event of a form control in an Angular application. The valueChanges event is emitted by the FormControl class whenever the value of the control changes.

If you are trying to test the valueChanges event and you are finding that the event is not being executed when you dispatch an event for an <input> field, there are a few things you can try.

First, make sure that you are using the correct syntax to dispatch the event. To dispatch an event for an <input> field, you can use the dispatchEvent method of the HTMLElement class. Here is an example of how you might dispatch an input event for an <input> field:

const inputElement = fixture.debugElement.query(By.css('input')).nativeElement;
inputElement.dispatchEvent(new Event('input'));

Next, make sure that you have correctly subscribed to the valueChanges event of the form control. Here is an example of how you might do this:

formControl.valueChanges.subscribe(value => {
  console.log(value);
});

Finally, make sure that you have correctly initialized the form control and that it is connected to the <input> field in your template. If the form control is not correctly initialized or connected to the <input> field, the valueChanges event will not be emitted when the field value changes.

Platform Server – Attach cookies to HTTP requests

In an Angular application that is running on the server using the PlatformServer module, you can attach cookies to HTTP requests by adding an HttpHeaders object with a 'Cookie' header to the request options.

Here is an example of how you might attach a cookie to an HTTP request using the HttpClient service:

import { HttpClient, HttpHeaders } from '@angular/common/http';

...

const headers = new HttpHeaders().set('Cookie', 'my-cookie=value');

this.http.get('/api/data', { headers }).subscribe(
  data => console.log(data),
  error => console.error(error)
);

In this example, the HttpHeaders object is created with a 'Cookie' header set to 'my-cookie=value'. This header will be included in the HTTP request, and the server will receive the cookie along with the request.

Allow to pass data with router.navigate

In Angular, you can pass data to a route by using the data property of the route configuration object.

Here’s an example of how you can define a route with data:

const routes: Routes = [
  {
    path: 'users/:id',
    component: UserDetailComponent,
    data: {
      userId: 'id'
    }
  }
];

To pass the data to the route, you can use the navigate method of the Router service and pass the data as the second argument.

this.router.navigate(['users', userId], { state: { data: userData } });

In the component that is activated by this route, you can access the data using the ActivatedRoute service and the data property of the route snapshot.

this.route.snapshot.data.userId;

Alternatively, you can also use the queryParams property of the navigate method to pass data as query parameters.

this.router.navigate(['users', userId], { queryParams: { data: userData } });

In the component that is activated by this route, you can access the query parameters using the ActivatedRoute service and the queryParams property of the route.

this.route.queryParams.subscribe(params => {
  console.log(params.data);
});

patchValue of Select with value not in options keeps the form valid

It sounds like you are experiencing an issue with using the patchValue method on a form control that is a select element, and the value you are trying to patch is not present in the list of options for the select element.

By default, when you use the patchValue method to set the value of a form control, the form control will become marked as dirty and the form will be considered invalid if the value you are trying to patch is not present in the list of options for the select element.

One option you could try is to set the emitEvent parameter of the patchValue method to false, which will prevent the value change event from being emitted and the form control will not be marked as dirty. However, this will not cause the form to become valid if the patched value is not present in the list of options.

Another option you could try is to use the setValue method instead of the patchValue method. The setValue method will set the value of the form control and mark the form control as dirty, but it will also validate the form control and the form as a whole. If the value you are trying to set is not present in the list of options, the form control will become invalid and the form will be considered invalid as well.

Modular Auth with compat AuthGuards throws R3InjectorError (NullInjectorError: No provider for InjectionToken angularfire2.app.options!)

It sounds like you are trying to use Angular Modular Auth with compatible AuthGuards, but you are encountering an error with the InjectionToken for angularfire2.app.options.

The InjectionToken for angularfire2.app.options is a token that is used to provide the Firebase app configuration options to the AngularFire library. It is required for the AngularFire library to be able to connect to Firebase and perform actions such as authentication.

One possible cause of the error you are seeing could be that you have not properly provided the InjectionToken for angularfire2.app.options in your Angular module.

To fix this error, you will need to make sure that you have imported the AngularFireModule and provided the InjectionToken for angularfire2.app.options in your Angular module.

Here is an example of how you can do this:

import { AngularFireModule } from '@angular/fire';
import { AngularFireAuthModule } from '@angular/fire/auth';

const firebaseConfig = {
  apiKey: '<your-api-key>',
  authDomain: '<your-auth-domain>',
  databaseURL: '<your-database-url>',
  projectId: '<your-project-id>',
  storageBucket: '<your-storage-bucket>',
  messagingSenderId: '<your-messaging-sender-id>',
  appId: '<your-app-id>'
};

@NgModule({
  imports: [
    AngularFireModule.initializeApp(firebaseConfig),
    AngularFireAuthModule
  ]
})
export class AppModule { }

Change detection is not triggered by an Observable whose values are emitted from an ErrorHandler

It sounds like you are using an Observable in your Angular application and the values emitted by the Observable are not triggering change detection in your component.

One possible reason for this could be that you are using an ErrorHandler to handle errors in your application, and the values emitted by the Observable are being emitted from within the ErrorHandler.

By default, Angular’s change detection is not triggered by events that occur outside the Angular zone. The ErrorHandler runs outside the Angular zone, so any events that are emitted from within the ErrorHandler will not trigger change detection.

To fix this issue, you will need to manually trigger change detection in your component whenever you want to update the component based on the values emitted by the Observable.

You can do this by injecting the ChangeDetectorRef in your component and calling the detectChanges method on it whenever you want to trigger change detection.

Here is an example of how you can do this:

import { ChangeDetectorRef } from '@angular/core';

export class MyComponent {
  constructor(private cdr: ChangeDetectorRef) {}

  ngOnInit() {
    this.myObservable.subscribe(value => {
      this.myValue = value;
      this.cdr.detectChanges();
    });
  }
}

Add value change options to ControlValueAccessor writeValue and onChange

It sounds like you are trying to implement a custom form control in Angular using the ControlValueAccessor interface and you want to add options for handling value changes to the writeValue and onChange methods.

The ControlValueAccessor interface is used to provide a custom form control that can be used in template-driven or reactive forms in Angular. It has three methods: writeValue, registerOnChange, and registerOnTouched.

The writeValue method is used to update the value of the form control. It takes a single argument, which is the new value for the form control.

The onChange method is a callback function that is called whenever the value of the form control changes. It takes a single argument, which is the new value of the form control.

You can add options for handling value changes to the writeValue and onChange methods by adding additional arguments to these methods and using these arguments to determine how to handle the value changes.

For example, you could add an options argument to the writeValue method and use this argument to specify whether to trigger change detection or not when the value of the form control is updated.

Here is an example of how you could do this:

import { ControlValueAccessor } from '@angular/forms';

export class MyCustomControl implements ControlValueAccessor {
  writeValue(value: any, options?: { triggerChangeDetection: boolean }): void {
    this.value = value;
    if (options && options.triggerChangeDetection) {
      this.cdr.detectChanges();
    }
  }

  registerOnChange(fn: (value: any) => void): void {
    this.onChange = (value: any, options?: { triggerChangeDetection: boolean }) => {
      fn(value);
      if (options && options.triggerChangeDetection) {
        this.cdr.detectChanges();
      }
    };
  }

  registerOnTouched(fn: () => void): void {
    this.onTouched = fn;
  }
}

Binding to the src of an iframe causes the iframe to flicker

You are binding the src attribute of an iframe element to a value in your Angular template and you are experiencing a flicker effect when the value of the binding changes.

One possible reason for this behavior is that the iframe is being reloaded every time the value of the src binding changes. This can cause the flicker effect as the iframe is briefly displayed with the old content before being replaced with the new content.

To fix this issue, you can try using the ngIf directive to conditionally render the iframe element based on the value of the src binding. This will prevent the iframe from being reloaded every time the src binding changes, and should eliminate the flicker effect.

Here is an example of how you can do this:

<iframe *ngIf="src" [src]="src"></iframe>

Alternatively, you can try using the DomSanitizer to sanitize the src binding before binding it to the iframe element. This can help prevent the iframe from being reloaded when the src binding changes, and may also help to eliminate the flicker effect.

Here is an example of how you can do this:

import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';

export class MyComponent {
  safeSrc: SafeResourceUrl;

  constructor(private sanitizer: DomSanitizer) {}

  ngOnChanges() {
    this.safeSrc = this.sanitizer.bypassSecurityTrustResourceUrl(this.src);
  }
}


<iframe [src]="safeSrc"></iframe>

Request: Method to pass environment variables during build vs file.

In Angular, you can use the environment file to define environment-specific configurations for your application. The environment file is a separate file for each environment (e.g. environment.prod.ts, environment.dev.ts) and is used to define variables that are specific to that environment.

There are a few different ways you can pass environment variables to your Angular application during the build process:

  1. Use the Angular CLI’s --configuration flag: You can use the --configuration flag to specify the environment when building your application. For example, to build for the production environment, you can run the following command: ng build --configuration=prod
  2. Use the ng-build-prod and ng-build-dev scripts: You can use the ng-build-prod and ng-build-dev scripts in your package.json file to build your application for the production and development environments, respectively.
  3. Use a custom build script: You can create a custom build script that sets the environment variables and builds your application. For example:
#!/usr/bin/env bash
export MY_ENV_VAR=foo
ng build

by default not send undefined params in url

One way to solve this problem is to use the HttpParams class to build the query parameters for your HTTP requests. The HttpParams class allows you to set the value of a query parameter to null or undefined, which will cause it to be excluded from the URL.

Here is an example of how you can use the HttpParams class to build the query parameters for an HTTP request:

import { HttpClient, HttpParams } from '@angular/common/http';

export class MyService {
  constructor(private http: HttpClient) {}

  getData(param1: string, param2: string) {
    let params = new HttpParams();
    if (param1) {
      params = params.set('param1', param1);
    }
    if (param2) {
      params = params.set('param2', param2);
    }

    return this.http.get('/api/data', { params });
  }
}

In this example, the param1 and param2 query parameters will only be added to the URL if their values are not null or undefined.

Schema validation failed: Data path “” should NOT have additional properties

It sounds like you are experiencing a problem with schema validation in your Angular application, and the error message you are seeing is “Schema validation failed: Data path “” should NOT have additional properties”.

This error typically occurs when you are trying to validate an object against a JSON schema, and the object contains properties that are not defined in the schema.

To fix this problem, you will need to make sure that the object you are trying to validate conforms to the schema by only including properties that are defined in the schema.

Here is an example of how you can do this:

const schema = {
  type: 'object',
  properties: {
    name: { type: 'string' },
    age: { type: 'number' }
  },
  required: ['name']
};

const objectToValidate = {
  name: 'John',
  age: 30,
  extraProp: 'foo' // this property is not defined in the schema
};

const validator = new Ajv();
const validate = validator.compile(schema);
const valid = validate(objectToValidate);

if (!valid) {
  console.log(validate.errors);
}

In this example, the object being validated contains the extraProp property, which is not defined in the schema. This will cause the validation to fail with the error message “Data path “” should NOT have additional properties”.

Add readonly to FormControl formstate

To add the readonly attribute to a FormControl in Angular, you can use the setAttribute method of the DOM element in the formControlName directive:

<input type="text" [formControlName]="controlName" readonly>

If you want to toggle the readonly attribute based on the value of a boolean property in your component’s class, you can bind to the readonly attribute using the [attr.readonly] syntax:

<input type="text" [formControlName]="controlName" [attr.readonly]="isReadonly">

You can then set the value of isReadonly in your component class to toggle the readonly attribute.

export class MyComponent {
  isReadonly = true;
  // ...
}

Alternatively, you can use the [readonly] binding to bind the readonly attribute to a boolean property in your component’s class:

<input type="text" [formControlName]="controlName" [readonly]="isReadonly">

This will set the readonly attribute to true if isReadonly is truthy, and remove it if isReadonly is falsy.

RouterLinkActiveOptions – Allow activating a link on multiple routes

To allow a RouterLinkActive directive to activate a link on multiple routes in Angular, you can pass an object to the [routerLinkActiveOptions] input with the exact property set to false. This will cause the RouterLinkActive directive to set the active class on the link element if the current route starts with the route path specified in the routerLink directive.

Here’s an example:

<a routerLink="/path1" routerLinkActive="active" [routerLinkActiveOptions]="{exact: false}">Link</a>

This will set the active class on the a element if the current route is either /path1 or a child route of /path1, such as /path1/child.

You can also specify multiple routes in the routerLink directive, separated by a comma. The RouterLinkActive directive will set the active class on the link element if the current route matches any of the routes in the routerLink directive.

<a routerLink="/path1,/path2" routerLinkActive="active">Link</a>

This will set the active class on the a element if the current route is either /path1 or /path2.

Ivy: Render HTML from String / dynamic rendering

To render HTML from a string in Angular, you can use the DomSanitizer service and the bypassSecurityTrustHtml method to tell Angular that the HTML is safe to display. Here’s an example of how you can use this method:

  1. Inject the DomSanitizer service into your component or service:
constructor(private sanitizer: DomSanitizer) {}
  1. Use the bypassSecurityTrustHtml method to tell Angular that the HTML is safe to display:
const htmlString = '<p>Hello, world!</p>';
this.safeHtml = this.sanitizer.bypassSecurityTrustHtml(htmlString);
  1. In your template, bind to the safeHtml property and use the innerHtml attribute to render the HTML:
<div [innerHtml]="safeHtml"></div>

Note that this method will bypass Angular’s built-in security checks, so you should make sure that the HTML you’re rendering is safe and cannot be used to perform a cross-site scripting (XSS) attack.

The “value” property on radio buttons with FormControl doesn’t get updated

To solve the problem of the “value” property not getting updated on radio buttons with a FormControl in Angular, you can try the following solutions:

  1. Use the setValue method to update the value of the FormControl:
this.formControl.setValue('new value');
  1. Use the patchValue method to update the value of a specific form control in a FormGroup:
this.formGroup.patchValue({controlName: 'new value'});
  1. Use the [(ngModel)] two-way binding syntax to bind the value of the radio button to a component property, and update the component property directly:
<input type="radio" [(ngModel)]="componentProperty">

this.componentProperty = 'new value';

Input element’s value attribute is not set

To solve the problem of the input element’s value attribute not being set in Angular, you can try the following solutions:

  1. Use the [value] binding to bind the value of the input to a component property:
<input type="text" [value]="componentProperty">

this.componentProperty = 'new value';
  1. Use the [(ngModel)] two-way binding syntax to bind the value of the input to a component property:
<input type="text" [(ngModel)]="componentProperty">

this.componentProperty = 'new value';
  1.  Use the FormControl and FormControlName directives to bind the value of the input to a form control
<input type="text" [formControl]="formControl">

this.formControl.setValue('new value');

ngcc – failing to compile dependencies and own libraries with secondary entries

You’re encountering an issue with the ngcc tool when trying to compile dependencies and own libraries with secondary entries in Angular. Here are a few things you can try to troubleshoot this issue:

  1. Make sure that you are running the latest version of ngcc and the Angular compiler. You can update these tools by running the following command:
npm install -g @angular/compiler-cli @angular/platform-server
  1. Check for any conflicts between the secondary entry points in your own libraries and the dependencies. If there are conflicts, you may need to modify your libraries to use unique names for their secondary entry points.
  2. Make sure that all of the required dependencies are correctly installed and that there are no issues with your package.json file.
  3. If you are using the Angular CLI, try running the ng build command with the --prod flag to see if that helps.

Improve router work with `history.state`

There are a few ways you can improve the performance of the router when using the history.state property in Angular:

  1. Avoid using the history.state property in your route configuration. Instead, you can use route parameters to pass data between routes. This will avoid the need to modify the history.state property, which can be slow.
  2. Use the runGuardsAndResolvers property of the ExtraOptions object to optimize the performance of route guards and resolvers. This property can be set to either paramsChange, paramsOrQueryParamsChange, or always, depending on your needs.
  3. Use the preload option of the PreloadingStrategy service to preload lazy-loaded routes in the background. This can help improve the performance of your application by reducing the amount of time it takes for these routes to load.
  4. Use the data property of the Route object to store any data that you need to pass between routes. This can be a more efficient way to pass data between routes than using the history.state property.

canActivate=>false can result in blank screen/dead link

If you are experiencing a blank screen or dead link issue when using the canActivate route guard in Angular, it is likely that you are not properly handling the false return value. When the canActivate guard returns false, the router will not navigate to the requested route.

To solve this issue, you can try the following approaches:

  1. Redirect the user to a different route if the canActivate guard returns false. You can use the Router service to navigate to a different route:
import { Router } from '@angular/router';

constructor(private router: Router) {}

canActivate(): boolean {
  if (this.isAuthenticated()) {
    return true;
  } else {
    this.router.navigate(['/login']);
    return false;
  }
}
  1. Display an error message or some other notification to the user if the canActivate guard returns false. You can use an element in your template to display the message:
<div *ngIf="!isAuthenticated">
  You are not authorized to view this page.
</div>

HttpParameterCodec improperly encodes special characters like ‘+’ and ‘=’

To solve this issue, you can try the following approaches:

  1. Use the encodeURIComponent() function to encode the special characters before sending the request:
import { HttpParams } from '@angular/common/http';

const params = new HttpParams()
  .set('param1', encodeURIComponent('value1+value2'))
  .set('param2', encodeURIComponent('value3=value4'));

this.http.get('/api/endpoint', { params });
  1. Use the HttpUrlEncodingCodec class to encode the special characters in the query string of the request:
import { HttpParams, HttpUrlEncodingCodec } from '@angular/common/http';

const params = new HttpParams({ encoder: new HttpUrlEncodingCodec() })
  .set('param1', 'value1+value2')
  .set('param2', 'value3=value4');

this.http.get('/api/endpoint', { params });

Clarify information about deprecation of :ng-deep and recommend replacement

The :ng-deep pseudo-class has been deprecated in Angular and is no longer recommended for use. This pseudo-class was used to apply styles to elements that are not directly targeted by a component’s styles, but it has been replaced by the ::ng-deep pseudo-class, which has similar behavior.

The ::ng-deep pseudo-class works in a similar way to :ng-deep, but it has been updated to comply with the latest standards for shadow-piercing combinators. It allows you to apply styles to elements that are not directly targeted by a component’s styles, but it does not pierce through the shadow boundary of a component’s view.

It is recommended to use the ::ng-deep pseudo-class instead of :ng-deep to avoid potential issues with the deprecation of :ng-deep. Here is an example of how you can use the ::ng-deep pseudo-class:

::ng-deep .some-class {
  /* styles go here */
}

valueChanges observable on AbstractControl emits events before its parent’s value has changed.

  1. Use the valueChanges observable of the parent form instead of the control. This observable will only emit events after the value of the parent form has changed.
  2. Use the statusChanges observable of the parent form instead of the valueChanges observable. This observable will emit an event after the value and validity of the form have been updated.
  3. Use the ngModelChange event of the control to detect changes to the control’s value. This event will only be emitted after the value of the control has been updated by the parent form.

Angular – Form Validation (duplicate trigger function custom validate)

To implement custom form validation in Angular, you can use the ValidatorFn and AsyncValidatorFn functions. These functions allow you to create custom validation logic for your forms and can be used in conjunction with the built-in validators provided by Angular.

Here’s an example of how you can use the ValidatorFn function to create a custom validator that checks for duplicate values:

import { FormControl } from '@angular/forms';

function duplicateValueValidator(control: FormControl): {[key: string]: any} | null {
  const value = control.value;
  if (value === 'duplicate') {
    return { 'duplicateValue': true };
  }
  return null;
}

You can then use this validator in your form like this:

this.form = new FormControl('', [duplicateValueValidator]);

You can also use the AsyncValidatorFn function to create async custom validators. This is useful if your validation logic requires making an async call, such as an HTTP request.

Http ResponseType cannot be set

In Angular, the ResponseType of an HTTP request can be set using the responseType property of the HttpRequest options object. Here’s an example of how you can set the ResponseType of an HTTP request in Angular:

import { HttpClient, HttpRequest, HttpResponse } from '@angular/common/http';

const options = {
  responseType: 'text'
};

const req = new HttpRequest('GET', '/api/endpoint', options);

this.http.request(req).subscribe((res: HttpResponse<string>) => {
  console.log(res.body);
});

The possible values for the responseType property are:

  • 'arraybuffer'
  • 'blob'
  • 'json'
  • 'text'

Use of `null` in `async` pipe has become problematic with NG12, TS4 and strict type checks

In Angular, the async pipe is used to subscribe to an observable or promise and automatically unsubscribe when the component is destroyed. When the observable or promise emits a value, the async pipe updates the template with the latest value.

Starting in Angular 12, the use of null values in the async pipe has become problematic due to the stricter type checks introduced in TypeScript 4. This is because the async pipe expects a value of type T | null, where T is the type of the value emitted by the observable or promise.

To solve this issue, you can either update your code to handle null values properly, or you can use the ngIf directive to handle the null values in your template. Here’s an example of how you can use the ngIf directive to handle null values:

<ng-container *ngIf="observable$ | async as value; else loading">
  {{ value }}
</ng-container>

<ng-template #loading>
  Loading...
</ng-template>

ng add @angular/fire -> Package install failed

It looks like you are encountering an issue when trying to install the @angular/fire package using the ng add command in Angular. Here are a few things you can try to troubleshoot this issue:

  1. Make sure that you are using the latest version of the Angular CLI. You can update the Angular CLI by running the following command:
npm install -g @angular/cli
  1. Make sure that you have an up-to-date version of Node.js installed on your system.
  2. Check your internet connection to ensure that you are able to download packages from npm.
  3. Try running the ng add command with the --force flag to force the installation of the package.
  4. If the issue persists, you may want to try deleting the node_modules folder and running npm install again.

Creating a project with existing directory => impossible?

It is possible to create an Angular project in an existing directory. Here’s how you can do it:

  1. Make sure you have the Angular CLI installed. If you don’t have it installed, you can install it by running the following command:
npm install -g @angular/cli
  1. Navigate to the directory where you want to create the project.
  2. Run the following command to create a new Angular project:
ng new my-project

Replace “my-project” with the name you want to give to your project. This will create a new Angular project in the current directory.

  1. If you want to create the project in a subdirectory of the current directory, you can specify the subdirectory name as follows:
ng new my-project --directory my-subdirectory

This will create the project in a subdirectory called “my-subdirectory” inside the current directory.

HttpClient sends the wrong Accept header

By default, the Angular HttpClient sets the “Accept” header to “application/json”. If you want to change the value of the “Accept” header, you can use the HttpHeaders class to create a new header and pass it to the HttpClient as follows:

import { HttpClient, HttpHeaders } from '@angular/common/http';

...

const headers = new HttpHeaders().set('Accept', 'application/xml');

this.http.get('/api/data', { headers: headers }).subscribe(data => {
  console.log(data);
});

You can also set the “Accept” header for all requests by default by using the HttpClient’s default options. To do this, you can use the HttpClient’s setGlobalHeaders method as follows:

import { HttpClient, HttpHeaders } from '@angular/common/http';

...

const headers = new HttpHeaders().set('Accept', 'application/xml');

this.http.setGlobalHeaders(headers);

this.http.get('/api/data').subscribe(data => {
  console.log(data);
});

This will set the “Accept” header to “application/xml” for all requests made using the HttpClient.

There is no FormControl instance attached to form control element with name: xxx

This error message appears when you are trying to access a FormControl that is not a part of your form.

Here are some possible reasons for this error:

  1. You may have forgot to add the FormControl to your form. Make sure that you have added the FormControl instance to your form’s FormGroup using the formGroupName directive.
  2. You may have misspelled the name of the FormControl. Make sure that the name of the FormControl in your template matches the name of the FormControl instance in your component class.
  3. You may have forgotten to import the necessary modules. Make sure that you have imported the FormsModule and ReactiveFormsModule in your module and included them in the imports array.
  4. You may have multiple forms on your page and are trying to access a FormControl from the wrong form. Make sure that you are accessing the correct form by using the formGroup directive.

HttpClient does not set X-XSRF-Token on Http Post

There are a few things that you can try to fix this issue:

  1. Make sure that you have enabled the withCredentials option on your HTTP requests. This is necessary for the XSRF-TOKEN cookie to be sent with your requests. You can enable this option by setting the withCredentials property to true on your HttpClient instance:
this.http.post(url, data, {withCredentials: true})
  1. Make sure that you have set the xsrfCookieName and xsrfHeaderName properties on your HttpClientXsrfModule configuration. These properties should match the name of the XSRF-TOKEN cookie and the name of the X-XSRF-TOKEN header, respectively.
{ provide: HTTP_INTERCEPTORS, useClass: HttpClientXsrfModule, multi: true, deps: [CookieXSRFStrategy, XSRFStrategy] },
  1. If you are using a custom XSRF-TOKEN cookie or header name, you can specify these names in the HttpClientXsrfModule configuration as well:
{ provide: XSRF_COOKIE_NAME, useValue: 'my-xsrf-cookie' },
{ provide: XSRF_HEADER_NAME, useValue: 'my-xsrf-header' },

Class requires Angular annotation even when abstract.

This error occurs when you are trying to create an abstract class in Angular that does not have a decorator, such as @Component or @Injectable.

In Angular, every class must have a decorator, even if it is an abstract class. This is because the decorator provides important metadata about the class, such as its dependencies and how it should be compiled by the Angular compiler.

To fix this error, you can simply add the @Injectable() decorator to your abstract class. This will allow you to create an abstract class in Angular without having to define any of the methods or properties that are typically required by the decorator.

Here’s an example of how you can do this:

@Injectable()
abstract class MyAbstractClass {
  // Your abstract class code here
}

Change detection not triggering for template event handler

This issue can occur when you are using an event handler in your template, but the event handler is not causing a change detection cycle to run. This can be because the event handler is not a pure function or because you are using an event binding with a native DOM event.

Here are some ways to fix this issue:

  1. Use a pure function as your event handler. A pure function is a function that always returns the same output for the same input and does not have any side effects. This means that the function does not modify any external state and does not perform any asynchronous operations. By using a pure function as your event handler, you can ensure that the event handler will trigger a change detection cycle whenever it is called.
  2. Use an event binding with an Angular event. Angular provides a set of events that you can use in your templates, such as (ngModelChange) and (ngSubmit). These events are designed to work with Angular’s change detection system and will trigger a change detection cycle whenever they are emitted.
  3. Manually trigger a change detection cycle. You can use the ChangeDetectorRef service to manually trigger a change detection cycle in your component. This can be useful if you are using a native DOM event or if you want to trigger a change detection cycle after an asynchronous operation has completed.

Allow router configuration so that preserveQueryParams is true by default

To set the preserveQueryParams option to true by default for all routes in your application, you can use the RouterModule.forRoot() method and provide a configuration object with the preserveQueryParams option set to true.

Here’s an example of how you can do this:

@NgModule({
  imports: [
    RouterModule.forRoot(routes, { preserveQueryParams: true })
  ],
  exports: [RouterModule]
})
export class AppRoutingModule { }

Alternatively, you can use the ExtraOptions interface to define a default configuration for all routes in your application. The ExtraOptions interface allows you to specify the number of options, including the preserveQueryParams option.

Here’s an example of how you can use the ExtraOptions interface:

const routerOptions: ExtraOptions = {
  preserveQueryParams: true,
  // Other options here
};

@NgModule({
  imports: [RouterModule.forRoot(routes, routerOptions)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

Combine multiple “| async as” in the same ngIf

You can use the as keyword and the ngIf directive to bind multiple async pipes to local variables in your template.

Here’s an example of how you can do this:

<ng-container *ngIf="observable1$ | async as value1; else loading1">
  {{ value1 }}
</ng-container>
<ng-template #loading1>Loading 1...</ng-template>

<ng-container *ngIf="observable2$ | async as value2; else loading2">
  {{ value2 }}
</ng-container>
<ng-template #loading2>Loading 2...</ng-template>
In this example, the observable1$ and observable2$ observables are bound to the value1 and value2 local variables, respectively, using the async pipe. The ngIf directive is used to conditionally render the values of value1 and value2 if the observables have emitted a value, and the else clause is used to display a loading message while the observables are still in the pending state.

Route redirectTo as function/class

In Angular, you can use a function or a class as the value of the redirectTo property in a route definition. This can be useful if you want to dynamically determine the target route based on some logic.

Here’s an example of how you can use a function as the redirectTo value:

const routes: Routes = [
  {
    path: '',
    redirectTo: () => {
      if (someCondition) {
        return '/route1';
      } else {
        return '/route2';
      }
    },
    pathMatch: 'full'
  }
];

Here’s an example of how you can use a class as the redirectTo value:

class Redirector {
  static readonly route = '/route1';
}

const routes: Routes = [
  {
    path: '',
    redirectTo: Redirector,
    pathMatch: 'full'
  }
];

In both examples, the redirectTo value is either a function or a class that returns the target route for the redirect. The pathMatch property is used to specify that the redirect should only occur when the entire path is empty.

Select field in Safari displays option but does not set value when default value is null

This issue can occur when you are using a select field in Angular with a default value of null, and you are trying to set the value of the select field using the [value] binding.

In Safari, the [value] binding does not work properly when the default value of the select field is null. To fix this issue, you can use the [ngValue] binding instead of the [value] binding. The [ngValue] binding works correctly in Safari and allows you to set the value of the select field even when the default value is null.

Here’s an example of how you can use the [ngValue] binding:

<select [ngModel]="selectedValue" (ngModelChange)="onChange($event)">
  <option [ngValue]="null">Select an option</option>
  <option *ngFor="let option of options" [ngValue]="option.value">{{ option.label }}</option>
</select>

In this example, the [ngValue] binding is used to bind the value of each option to the option.value property. The [ngModel] directive is used to bind the value of the select field to the selectedValue variable, and the (ngModelChange) event is used to update the selectedValue variable when the value of the select field changes.

ng build throw error – Cannot assign value “data” to template variable “permission”. Template variables are read-only.

This error is typically caused by attempting to reassign a value to a template variable within an Angular template. Template variables are read-only and cannot be reassigned.

To fix this error, you’ll need to remove any attempts to reassign values to template variables within your templates. Instead, you should use a component property or a local variable to store the value that you want to use in the template.

Here is an example of how you could modify your template to use a component property instead of a template variable:

<!-- Before -->
<ng-template #permission="data">
  {{ permission }}
</ng-template>

<!-- After -->
<ng-template>
  {{ componentProperty }}
</ng-template>

Then, you can assign a value to the componentProperty in your component class:

export class MyComponent {
  componentProperty = 'data';
}

Allow Route Params to be Passed into Component @Inputs

To allow route params to be passed into a component’s @Input() properties, you will need to do the following:

  1. Inject the ActivatedRoute service into your component
  2. Subscribe to changes in the route params
  3. Use the route params to set the values of your component’s @Input() properties

Here is an example of how you could implement this in your component:

import { Component, OnInit, Input } from '@angular/core';
import { ActivatedRoute } from '@angular/router';

@Component({
  selector: 'app-my-component',
  templateUrl: './my-component.component.html',
  styleUrls: ['./my-component.component.css']
})
export class MyComponent implements OnInit {
  @Input() param1: string;
  @Input() param2: string;

  constructor(private route: ActivatedRoute) { }

  ngOnInit() {
    this.route.params.subscribe(params => {
      this.param1 = params['param1'];
      this.param2 = params['param2'];
    });
  }
}

Then, you can bind to these input properties in your template:

<p>param1: {{ param1 }}</p>
<p>param2: {{ param2 }}</p>

You can also use the paramMap property of the ActivatedRoute instead of the params property if you prefer. The paramMap is an observable of the route’s param map, which can be used to get the value of the route params.

this.route.paramMap.subscribe(paramMap => {
  this.param1 = paramMap.get('param1');
  this.param2 = paramMap.get('param2');
});

Ugraded to Angular 9: Error: Can’t resolve all parameters for ApplicationModule: (?).

This error is typically caused by one of the following issues:

  1. A missing or incorrect import statement for a module or service that is being injected into a component or service.
  2. A missing or incorrect provider for a service that is being injected into a component or service.
  3. A circular dependency between modules or services.

To fix this error, you will need to identify and resolve the issue that is causing the error. Here are some steps you can take:

  1. Check the import statements in your code to make sure that you are correctly importing all required modules and services.
  2. Check the provider lists in your module and component decorators to make sure that you have provided all required services.
  3. Check for circular dependencies between your modules and services. A circular dependency occurs when a module or service depends on itself, either directly or indirectly.

FormControl valueChanges should emit null instead of previous value on control.disabled()

It is expected behavior for the FormControl valueChanges observable to emit the previous value when the control is disabled. This is because the value of the control has not actually changed, so the valueChanges observable emits the previous value to indicate that the value has not changed.

If you want the valueChanges observable to emit null when the control is disabled, you can map the valueChanges observable through the map operator and return null when the control is disabled.

Here is an example of how you could do this:

import { map } from 'rxjs/operators';

control.valueChanges.pipe(
  map(value => control.disabled ? null : value)
).subscribe(value => {
  // value will be null when the control is disabled
});

[feature] Enable/disable event listening on @HostListeners

The @HostListener decorator allows you to attach event listeners to the host element of the component. If you want to enable or disable event listening for a particular @HostListener decorator, you can use the enableEventListeners flag.

Here is an example of how you can use the enableEventListeners flag:

import { HostListener } from '@angular/core';

@Component({
  selector: 'my-component',
  template: `...`
})
export class MyComponent {
  private enableEventListeners = true;

  @HostListener('click', ['$event'])
  onClick(event: Event) {
    if (this.enableEventListeners) {
      // do something
    }
  }

  enable() {
    this.enableEventListeners = true;
  }

  disable() {
    this.enableEventListeners = false;
  }
}

You can then call the enable() and disable() methods to enable or disable the event listener.

Form statusChanges with async validators doesn’t leave PENDING state

It is expected behavior for the FormControl’s statusChanges observable to remain in the PENDING state until all async validators have completed. This is because the statusChanges observable emits a new value every time the control’s validation status changes, and the control is considered to be in the PENDING state while async validators are running.

Once all async validators have completed, the statusChanges observable will emit a new value with the control’s updated validation status. If the control is now valid, the statusChanges observable will emit a value of VALID. If the control is now invalid, the statusChanges observable will emit a value of INVALID.

Here is an example of how you can use the statusChanges observable with async validators:

import { take } from 'rxjs/operators';

control.statusChanges.pipe(
  take(1) // take only the first value
).subscribe(status => {
  if (status === 'VALID') {
    // the control is now valid
  } else if (status === 'INVALID') {
    // the control is now invalid
  } else {
    // the control is in the PENDING state
  }
});

In this example, the statusChanges observable is piped through the take operator, which ensures that the subscription only receives the first value emitted by the observable. This means that the subscription will complete after the first validation status change, and will not continue to listen for future changes.

Firebase throwing auth/internal-error when initializing without the Internet connection

If you are using the Firebase AngularFire library and you see the auth/internal-error error when initializing the Firebase SDK without an internet connection, you can try using the following workaround:

  1. Import the firebase/app library and initialize the Firebase app directly:
import * as firebase from 'firebase/app';

firebase.initializeApp({
  apiKey: '...',
  authDomain: '...',
  // other options
});
  1. Import the firebase/auth library and use the firebase.auth().setPersistence method to set the persistence mode to firebase.auth.Auth.Persistence.NONE:
import 'firebase/auth';

firebase.auth().setPersistence(firebase.auth.Auth.Persistence.NONE);

This will allow you to use the Firebase AngularFire library in offline mode, without requiring an internet connection.

Elements lazy load make unable to use providers in modules

In Angular, lazy loading means that a module and its dependencies are loaded only when the user navigates to a route that is associated with that module. Lazy loading can improve the performance of your Angular application by reducing the amount of code that needs to be loaded initially.

If you are unable to use providers in a lazy-loaded module, it may be because the provider is not being properly imported into the module. In order to use a provider in a lazy-loaded module, you will need to make sure that the provider is either provided by the root module or is imported into the lazy-loaded module.

Here is an example of how you might import a provider into a lazy-loaded module:

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MyService } from './my.service';

@NgModule({
  imports: [
    CommonModule
  ],
  providers: [MyService]
})
export class MyModule { }

You can then use the MyService provider in any component that is a part of the MyModule module.

ExpressionChangedAfterItHasBeenCheckedError with dynamic validators in a template driven composite form control

The ExpressionChangedAfterItHasBeenCheckedError error typically occurs when a component’s view is being checked after its state has changed. This can happen if you are trying to change the value of a template expression after Angular has finished checking the component’s view.

One common cause of this error is trying to add or remove dynamic validators in a template-driven form. When using template-driven forms, Angular performs form validation during the change detection cycle. If you try to change the validators for a form control after Angular has finished checking the form, you will get this error.

To avoid this error, you should make sure to add or remove dynamic validators before Angular begins checking the form. One way to do this is to use the ngOnInit lifecycle hook to set up your form controls and validators.

Here is an example of how you might use ngOnInit to set up a template-driven form with dynamic validators:

import { Component, OnInit } from '@angular/core';
import { FormControl, Validators } from '@angular/forms';

@Component({
  selector: 'app-my-form',
  template: `
    <form>
      <input type="text" [formControl]="myControl" />
    </form>
  `
})
export class MyFormComponent implements OnInit {
  myControl = new FormControl();

  ngOnInit() {
    this.myControl.setValidators([Validators.required]);
  }
}

By setting up your form controls and validators in ngOnInit, you can ensure that they are set up before Angular begins checking the form.

Router: Feature Request: default ‘router-link-active’ class attachment (old router behaviour)

In Angular, the routerLinkActive directive adds a class to the element when the associated router link becomes active. By default, the class name is router-link-active.

If you would like to change the default class name that is applied when the router link is active, you can set the routerLinkActiveOptions input of the routerLinkActive directive. For example:

<a routerLink="/my-route" routerLinkActive="active-link" [routerLinkActiveOptions]="{exact: true}">My Route</a>

This would apply the active-link class to the a element when the router link is active and the current URL is an exact match for the /my-route route.

If you would like to revert to the old behavior of the routerLinkActive directive, where the router-link-active class is applied by default, you can set the routerLinkActiveOptions input to an empty object:

<a routerLink="/my-route" routerLinkActive [routerLinkActiveOptions]="{}">My Route</a>

This will apply the router-link-active class to the a element when the router link is active, regardless of whether the current URL is an exact match for the route.

Share

It’s Really not that Complicated.

You can actually understand what’s going on inside your live applications.

Try Lightrun’s Playground

Lets Talk!

Looking for more information about Lightrun and debugging?
We’d love to hear from you!
Drop us a line and we’ll get back to you shortly.

By submitting this form, I agree to Lightrun’s Privacy Policy and Terms of Use.