Form control value is null when initialized with undefined
  • 25-Jan-2023
Lightrun Team
Author Lightrun Team
Share
Form control value is null when initialized with undefined

Form control value is null when initialized with undefined

Lightrun Team
Lightrun Team
25-Jan-2023

Explanation of the problem

Problem: In Angular, when a FormControl is initialized with an undefined value (initial state), the value of the FormControl#value is null instead of undefined.

Environment: The issue occurs in an Angular CLI version 14.1.0 environment, running on Node version 16.16.0 and npm version 8.15.1.

Steps to Reproduce: To reproduce the issue, follow these steps:

  1. Initialize a FormControl with an undefined value.
  2. Check the value of the FormControl using FormControl#value.
  3. The value will be null instead of undefined.

Expected Behavior: When initializing a FormControl with an undefined value, the FormControl#value should also be undefined.

Actual Behavior: The FormControl#value is null when initializing a FormControl with an undefined value.

Minimal Reproduction Link: A minimal reproduction of the issue can be found at the following link: https://stackblitz.com/edit/angular-ivy-x37dzv

Exception/Error: If you test the value of the FormControl initialized with undefined value using the code “expect(control.value).toBe(undefined)”, the test will fail.

Troubleshooting 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

Problem solution for FormGroup.disable() and FormGroup.enable() do not allow resetting disabled state in Angular Angular

The issue with FormControl in Angular, where initializing a FormControl with an undefined value results in the FormControl#value being null instead of undefined, is a known limitation of the Forms APIs. Changing this behavior to drop null as the default value would be a breaking change, and would require extra design work to figure out how that would affect the rest of the Forms logic. In the meantime, a potential workaround is to use additional APIs to define what the default value should be on a per-control basis or globally.

Regarding the use of null as a value for a nonNullable control, it should still be accepted. The nonNullable property only means that the reset() method won’t set the value of the control to null, but will instead set it to its initial value, which can be null. There are many control types that should be reset to their initial value when reset() is called, but which must accept null as their value, simply because the user can clear the input or because the input should be initially empty.

 

Other popular problems with Angular

Problem: Change Detection Performance

One of the most common problems with Angular is related to change detection performance. Angular uses a mechanism called change detection to update the view when the component’s data changes. However, if the component has a lot of bindings or the change detection runs too often, the performance can suffer. This can lead to slow rendering times and a poor user experience.

Solution:

One solution to this problem is to use the OnPush strategy for change detection. This strategy only runs change detection when an input property of the component changes, instead of running it for all bindings. To use the OnPush strategy, you need to set the changeDetection property of the component to ChangeDetectionStrategy.OnPush.

@Component({
  selector: 'app-my-component',
  template: '...',
  changeDetection: ChangeDetectionStrategy.OnPush
})

Another solution is to use the async pipe for bindings that are observables. This pipe automatically subscribes to the observable and unsubscribes when the component is destroyed, and also triggers change detection only when the observable emits a new value.

@Component({
  selector: 'app-my-component',
  template: `
    <div *ngFor="let item of items | async"></div>
  `
})
export class MyComponent {
  items: Observable<Item[]> = this.service.getItems();
}

Problem: Memory Leaks

Another common problem with Angular is related to memory leaks. Memory leaks occur when an object is no longer in use but is still being held in memory by other objects. This can happen when a component is not properly unsubscribed from observables or event emitters, or when a component is not properly cleaned up during the destruction process.

Solution:

To avoid memory leaks, it is important to unsubscribe from observables and event emitters when a component is destroyed. One way to do this is to use the ngOnDestroy lifecycle hook. In this hook, you can call the unsubscribe method of the subscription or use takeUntil operator.

export class MyComponent implements OnInit, OnDestroy {
  private unsubscribe$ = new Subject<void>();

  ngOnInit() {
    this.service.getItems()
      .pipe(takeUntil(this.unsubscribe$))
      .subscribe(items => this.items = items);
  }

  ngOnDestroy() {
    this.unsubscribe$.next();
    this.unsubscribe$.complete();
  }
}

Another way to avoid memory leaks is to use the async pipe for observables and event emitters. This pipe automatically unsubscribes when the component is destroyed.

<div *ngFor="let item of items | async"></div>

Problem: Routing and Navigation

Routing and navigation is one of the most important parts of any single-page application, and it’s also one of the most common sources of problems in Angular. Issues can range from routing configuration to navigation events and guards.

Solution:

One solution to this problem is to use the Angular Router module, which provides a powerful and flexible way to handle routing and navigation. This module provides a powerful way to configure routes and navigate between different parts of your application.

const routes: Routes = [
  { path: '', component: HomeComponent },
  { path: 'about', component: AboutComponent },
  { path: '**', component: PageNotFoundComponent }
];

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

Another solution is to use guards. Guards are classes that can be used to protect access to certain routes. They can be used to prevent a user from navigating to a route or to prompt the user for confirmation before navigating.

@Injectable()
export class CanActivateGuard implements CanActivate {
  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
    // code to check if the user is allowed to activate the route
    return true;
  }
}

In the routing configuration, the guard can be added to a route to protect it.

const routes: Routes = [
  { path: 'secret', component: SecretComponent, canActivate: [CanActivateGuard] }
];

This way the routing and navigation in the application can be handled properly and also it’s possible to restrict access to certain routes based on certain conditions.

A brief introduction to Angular

Angular is a JavaScript framework for building web applications. It is built on top of the popular JavaScript framework, AngularJS. Angular is a complete rewrite of AngularJS and is designed to be more efficient, modular, and easier to use. It is also built with TypeScript, a superset of JavaScript that provides static typing and other features that make it easier to write and maintain large applications.

Angular uses a component-based architecture, where an application is composed of a tree of components. Each component is responsible for a specific part of the application’s UI and logic. Components can also be nested, allowing for a hierarchical and modular structure. Angular also provides a powerful template language that allows developers to declaratively describe the UI of a component. The template language is tightly integrated with the component’s logic, making it easy to create complex and interactive UIs. Angular also provides a powerful set of directives and pipes that can be used to manipulate the DOM and transform data. The framework also provides a powerful set of services that can be used to share data and logic across the application.

Most popular use cases for Angular

  1. Building Single-Page Applications (SPAs): Angular can be used to build Single-Page Applications (SPAs). SPAs are web applications that load a single HTML page and dynamically update the content as the user interacts with the app. Angular provides a powerful set of tools for building SPAs, including a powerful router for handling client-side routing and navigation, and a powerful template language for describing the UI of the application.
import { RouterModule, Routes } from '@angular/router';

const routes: Routes = [
  { path: '', component: HomeComponent },
  { path: 'about', component: AboutComponent },
  { path: '**', component: PageNotFoundComponent }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }
  1. Building Progressive Web Applications (PWAs): Angular can be used to build Progressive Web Applications (PWAs). PWAs are web applications that can be installed on the user’s device and run offline. Angular provides a powerful set of tools for building PWAs, including a powerful service worker module that can be used to cache assets and provide offline support.
  2. Building Mobile Applications: Angular can be used to build mobile applications using technologies like Apache Cordova or NativeScript. This allows developers to create mobile apps using web technologies like HTML, CSS, and JavaScript, and then package them for various platforms such as iOS and Android. Angular provides a powerful set of tools for building mobile apps, including a powerful layout system that can be used to adapt the app’s UI to different screen sizes and resolutions.
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.