valueChanges-Event of Control is not executed when dispatching Event for Input-Field
  • 10-May-2023
Lightrun Team
Author Lightrun Team
Share
valueChanges-Event of Control is not executed when dispatching Event for Input-Field

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

Lightrun Team
Lightrun Team
10-May-2023

Explanation of the problem

The issue is related to unit testing of an Angular Typeahead component. The component is configured to use an input field with the ngFormControl directive bound to a searchTerm property which is of type Control. The searchTerm property is used in a chain of observables to make an HTTP call using taskService.findTasks() on valueChanges event of the input field. The issue is that the valueChanges event is not triggered when using dispatchEvent() to simulate user input in the input field. The test only passes when the searchTerm.updateValue() method is used to update the value of the searchTerm property directly.

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: [testing] valueChanges-Event of Control is not executed when dispatching Event for Input-Field

The issue seems to be related to the dispatchEvent() method not triggering the valueChanges event of the input field. To solve this issue, we can manually trigger the valueChanges event after updating the value of the input field using dispatchEvent(). This can be done by getting a reference to the input field using fixture.debugElement.nativeElement.querySelector() and then manually triggering the valueChanges event using the triggerEventHandler() method. Here is an example code block:

 

iit('should execute a Typeahead-Search', <any> fakeAsync(() => {
  fixture.detectChanges();
  let taskList = fixture.componentInstance,
    element = fixture.nativeElement;
  var spy : Spy = spyOn(taskService, 'findTasks').and.callThrough();
  let input = element.querySelector('#searchField');
  input.value = 'Term';
  dispatchEvent(input, 'input');
  fixture.detectChanges();
  tick(600);
  console.log(input.value); // prints "Term" so value was updated
  input.triggerEventHandler('valueChanges', null); // manually trigger valueChanges event
  expect(taskService.findTasks).toHaveBeenCalledWith({'q': 'Term'});
}));

In this code block, we have added a call to input.triggerEventHandler('valueChanges', null); after the dispatchEvent() method to manually trigger the valueChanges event of the input field.

This solution should allow us to properly simulate user input in the input field and trigger the valueChanges event in the Typeahead component, allowing the test to pass without having to update the value of the searchTerm property directly.

Environment:

  • Angular version: 2.0.0-beta.8
  • Language: TypeScript 1.8.7
  • Browser: Chrome or Karma

Problems with angular

  1. Problem: “Cannot read property ‘length’ of undefined” error when using the async pipe with a BehaviorSubject. Description: Users have reported an error when using the async pipe with a BehaviorSubject. The error message is “Cannot read property ‘length’ of undefined”. This error occurs when the BehaviorSubject has not yet emitted any values and the async pipe tries to access the length property of the value. Solution: One solution is to initialize the BehaviorSubject with a default value. For example:

 

private dataSubject = new BehaviorSubject<any[]>([]);
data$ = this.dataSubject.asObservable();

Then, in the component, check if the value is undefined before accessing its length property:

 

<ng-container *ngIf="data$ | async as data">
  <div *ngIf="data.length > 0">
    // Display data here
  </div>
</ng-container>

 

  1. Problem: Error “zone.js has detected that ZoneAwarePromise (window|global).Promise has been overwritten” when running Angular tests. Description: When running Angular tests, users have reported an error message that says “zone.js has detected that ZoneAwarePromise (window|global).Promise has been overwritten”. This error occurs when a third-party library, such as a polyfill, overwrites the global Promise object. Solution: One solution is to use the zone.js library to patch the global Promise object before running the tests. Add the following line to the test.ts file:

 

import 'zone.js/testing';

 

Then, in the beforeEach function of the test, patch the Promise object:

 

beforeEach(() => {
  (window as any)['Promise'] = Promise;
});

 

  1. Problem: Error “Maximum call stack size exceeded” when using ng serve. Description: Some users have reported an error message that says “Maximum call stack size exceeded” when running the ng serve command. This error occurs when there is a circular dependency in the application. Solution: One solution is to use the Angular dependency injection system to break the circular dependency. For example, if ComponentA depends on ComponentB and ComponentB depends on ComponentA, create a new service and move the shared logic to the service. Then, inject the service into both components. For example:

 

@Injectable({ providedIn: 'root' })
export class SharedService {
  // Shared logic here
}

@Component({...})
export class ComponentA {
  constructor(private sharedService: SharedService) {}
  // Use the shared service here
}

@Component({...})
export class ComponentB {
  constructor(private sharedService: SharedService) {}
  // Use the shared service here
}

A brief introduction to angular

Angular is a popular open-source web application framework used to build dynamic single-page applications. The Angular framework is written in TypeScript, a superset of JavaScript that allows for static typing and other features. The primary goal of Angular is to provide a robust and efficient framework for building web applications that can be easily maintained and scaled. Angular is known for its powerful features such as two-way data binding, dependency injection, and an extensive library of built-in directives and services.

One of the main benefits of Angular is its component-based architecture, where each component represents a specific feature or view in the application. Components are reusable and can be composed to create complex UIs. Angular also supports reactive programming and provides a robust set of APIs for working with observables, which allows for easier handling of asynchronous operations. The framework has a steep learning curve, but once mastered, developers can build complex and scalable web applications in a relatively short time. Overall, Angular is a versatile framework that offers a lot of power and flexibility to developers.

Most popular use cases for angular

    1. Building dynamic and scalable web applications: Angular provides a comprehensive framework for building modern web applications that are scalable, maintainable, and easy to develop. It offers a wide range of tools and features that make it easy to create dynamic and interactive web applications that can handle large amounts of data and complex workflows.
    2. Creating reusable UI components: Angular’s component-based architecture allows developers to create reusable UI components that can be easily integrated into different parts of an application. This feature makes it easier to maintain and update applications over time, as changes made to a component are automatically propagated to all instances of that component in the application.

    Here’s an example code block for creating a reusable component in Angular:

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

@Component({
  selector: 'app-button',
  template: `
    <button [class]="classes">
      {{label}}
    </button>
  `
})
export class ButtonComponent {
  @Input() label: string;
  @Input() classes: string;
}
  1. Developing cross-platform applications: With Angular, developers can build applications that can run on multiple platforms, including web, mobile, and desktop. This is made possible by using technologies such as Angular Universal for server-side rendering, and frameworks like Ionic for mobile app development. By leveraging these tools, developers can create a single codebase that can be used to build applications for multiple platforms.

Here’s an example code block for building a cross-platform application with Angular and Ionic:

 

# Install Ionic CLI
npm install -g @ionic/cli

# Create a new Ionic app with Angular
ionic start myApp --type=angular

# Add a new page to the app
ionic generate page myPage

# Build and run the app on a mobile device
ionic capacitor add ios
ionic capacitor copy ios
ionic capacitor open ios
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.