Updates to Angular form control values are not triggering notifications

I am currently working with a basic form control that subscribes to the valueChanges observable.

@Component({
  selector: 'my-app',
  template: `
       <input [formControl]="control" />
       <div>{{ name$ | async | json }}</div>
   `
})
export class AppComponent implements OnInit {
  name$: Observable<string>;
  control;

  constructor(private builder: FormBuilder) {
    this.control = this.builder.control('');
  }

  ngOnInit() {
    this.name$ = this.control.valueChanges
      .pipe(
        map((name) => {
          console.log('fired', name)
          return name;
        })
      );


    this.control.setValue('2');
  }
}

However, I have encountered an issue where when I call setValue, the observer does not receive any notification. It appears that the async pipe is not functioning as expected. Strangely, if I wrap the setValue method in a setTimeout, it starts working correctly. Can anyone shed some light on why this behavior occurs?

In an attempt to solve this issue, I also tried using the shareReplay() method, but unfortunately, it did not resolve the problem either.

Answer №1

This is a single solution to the problem:

this.control.setValue('2'); // Move any changes to the top for the startWith operator 
this.name$ = this.control.valueChanges
  .pipe(
    startWith(this.control.value),
    map((name) => {
      console.log('fired', name)
      return name;
    })
  );

Another approach to make it function without using the async operator is to subscribe to the value changes before the control value changes to '2' and capture all subsequent changes:

this.control.valueChanges
  .pipe(
    map((name) => {
      console.log('fired', name)
      return name;
    })
  )
  .subscribe ( v => this.value = v )

Template:

{{value}}

The reason the example with async doesn't work is that the async operator subscribes to the observable, but at the time of subscription, the current value of the control is '2', so no changes are emitted. Another point to note is that observables are lazy, so when you subscribe, you only get the current value, any previous changes are not visible.

Answer №2

The issue with the previous attempt was that the view was not initialized when the function was called from ngOnInit. To fix this, the code should be placed in the ngAfterViewInit() method:

import AfterViewInit from '@angular/core';
export class AppComponent implements AfterViewInit{ 
   ngAfterViewInit() {
    this.name$ = this.control.valueChanges
      .pipe(
        map((name) => {
          console.log('fired', name)
          return name;
        })
      );


    this.control.setValue('2');
  }
}

It is important to test the updated code to ensure its correctness.

Answer №3

If you're looking for a general solution to create an observable of a form control value in Angular:

this.value$ = defer(() => {
  return this.control.valueChanges.pipe(startWith(this.control.value));
});

When you use defer, it ensures that the startWith value will reflect the actual value of your form when the observable is subscribed to. Without defer, the startWith value would only be the form value at the time the observable is created, not when it's subscribed.

To make this reusable across your app, you can encapsulate this logic in a utility function:

// utils.ts
export function observeControlValue$(formControl: AbstractControl): Observable<any> {
    return defer(() => {
        return formControl.valueChanges.pipe(startWith(formControl.value));
    });
}

// component.ts
this.value$ = observeControlValue$(this.control);

Answer №4

When working with reactive forms, it's recommended to use FormControl instead of FormBuilder for initialization.

To initialize your control, follow this example :

export class AppComponent implements OnInit {
public control = new FormControl('');

In your HTML, use:

<input type="text" [formControl]="control">

With this setup, you won't need to use Observable<string>, just:

<div>{{ control.value | json }}</div>

For more details, refer to the ReactiveForm documentation: https://angular.io/guide/reactive-forms

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Adding form input fields in real-time by clicking the add button

Currently, I am looking to develop a party hosting application using Ionic 2. My main goal is to have the ability to add form input fields for guests dynamically by clicking on an "add" button. Do you have any suggestions on how this can be achieved in I ...

Filter and transfer data from one Angular array to another

As a newcomer to Angular, I am working with an array of events containing multiple arguments. My goal is to filter these events and separate them into two new arrays: upcoming events and past events. Below is a snippet of the TypeScript code I am using: a ...

"Experience the power of utilizing TypeScript with the seamless compatibility provided by the

I'm attempting to utilize jsymal.safeDump(somedata). So far, I've executed npm install --save-dev @types/js-yaml I've also configured my tsconfig file as: { "compilerOptions": { "types": [ "cypress" ...

What is the process for integrating unit tests from external sources into an Angular project following an upgrade to version 15

As of Angular v15, the require.context function has been removed from the test.ts configuration file. I used to rely on require.context to expose tests outside of the Angular project to Karma. Now that it's no longer available: const contextGlobal = ...

Winston's createLogger function is not defined

After setting up Jest unit tests in a TypeScript Node project, I encountered an issue where the main code broke after installing Jest with ts-node. Whenever I try to run npm test or npm start, I receive the following error: [nodemon] watching extensions: t ...

Utilizing ElementRef in Angular 4 to close dropdown when clicking outside of it

I recently came across this helpful tutorial, but I'm having trouble grasping how it actually functions. Here's the code snippet I've incorporated into my TypeScript file: @Component({ host: { '(document:click)': 'onOuts ...

When there is only one value, the BehaviorSubject can be hit multiple times

Recently, I implemented BehaviourSubject in a shared service to retrieve the current value when clicking a button. Everything seems to be working fine, however, there are instances where the API call within the subscribe block of BehaviourSubject is being ...

Issue with subscribing in a MEAN stack application

Currently, I have completed the backend development of my application and am now working on the frontend. My focus at the moment is on implementing the register component within my application. Below is the code snippet for my Register Component where I a ...

Is there a way to incorporate timeouts when waiting for a response in Axios using Typescript?

Can someone assist me in adjusting my approach to waiting for an axios response? I'm currently sending a request to a WebService and need to wait for the response before capturing the return and calling another method. I attempted to utilize async/aw ...

What is the best way to terminate a connection in @aspnet/signalr?

My project utilizes the dependency "@aspnet/signalr": "^1.1.4". Does anyone know how to properly close a connection in Angular's destructor? I attempted to use: this.connection.close(); ...

Changing the value of a textarea in Angular forms using JavaScript

Having trouble with Angular forms? Try this: document.querySelector(TEXTAREA_SELECTOR).value = "some text" Methods like .title, .name, or .innerText don't seem to work. Consider trying these: document.querySelector(TEXTAREA_SELECTOR).dispa ...

How to bring in a specific module using its name in TypeScript

Can a module in typescript import itself by its own name? For example, let's consider a module called my-module with various tests. Is it possible to import it within the tests using import ... from "my-module" instead of using a local path like impo ...

What is the best way to define a category in order to utilize a saved string as a variable for referencing it?

An object named CONFIG holds the following information: export const CONFIG = { buttonDestinations: { detailedStats: `detailedStats`, mealPlans: `mealPlans`, products: `products` }, buttonTexts: { detailedStats: ...

What is the best way to remove all validators from a different component in Angular 7 using reactive forms?

I need to find a way to clear all validation in a form group on a sibling component when a boolean value is selected on another component within the same application. Is there a solution for achieving this? We have implemented a method in our application ...

Having trouble rendering Next.JS dynamic pages using router.push()? Find out how to fix routing issues in your

The issue I am facing revolves around the visibility of the navbar component when using route.push(). It appears that the navbar is not showing up on the page, while the rest of the content including the footer is displayed. My goal is to navigate to a dy ...

Select a randomly generated number from an array, which dynamically updates every time the browser is refreshed

I recently completed a project in Angular that utilizes the TMDB API. The project is nearly finalized, but I have a desire to implement a change where the background image (backdrop_path) and other elements shift each time the browser is reloaded. Curren ...

Encountering issues with importing a module from a .ts file

Although I have experience building reactJS projects in the past, this time I decided to use Node for a specific task that required running a command from the command line. However, I am currently facing difficulties with importing functions from other fil ...

What is the best way to reproduce the appearance of a div from a web page when printing using typescript in Angular 4?

Whenever I try to print a div from the webpage, the elements of the div end up on the side of the print tab instead. How can I fix this issue? Below is my current print function: print(): void { let printContents, popupWin; printContents = document.getEl ...

Retrieving the value of an object using a key in TypeScript

Is there a way to create an object using keys from another object and determine the type based on a string? For example: const result = rebuild(query, { name: 'string' }); // query - { name: '123', dont_need: true } // result - { n ...

Incorporating a background image into a mat-dialog

After spending some time on the mat-dialog documentation, I discovered that I can add a background image to the mat-dialog using panelClass: 'my-class' to customize its appearance. This applies the class my-class to the div with the class cdk-ove ...