This article is about fixing Reactive Form change through ControlValueAccessor doesn't update view in Angular Angular
  • 24-Jan-2023
Lightrun Team
Author Lightrun Team
Share
This article is about fixing Reactive Form change through ControlValueAccessor doesn't update view in Angular Angular

Reactive Form change through ControlValueAccessor doesn’t update view in Angular Angular

Lightrun Team
Lightrun Team
24-Jan-2023

Explanation of the problem

The user is reporting a bug in Angular 2.4.1 that occurs when updating a value through a form field that uses the formControl directive. The issue is that the view is not updated when a value is changed through the form field. The user has provided a minimal reproduction of the problem in the form of a plunkr link http://plnkr.co/edit/LXyo6uHdbEpP3feudYoo?p=preview, which demonstrates the problem.

The user also mentions that this behavior does not occur when using NgModel and when calling formControl.setValue(newVal). This is causing the contract custom form fields have when using the ControlValueAccessor interface to be broken. An example provided is the http://valor-software.com/ng2-bootstrap/#/buttons#radio, which currently only works with NgModel as it requires updating the view on all relevant buttons on value change.

The user provides a solution to this problem, which is to use a set of buttons with reactive forms, currently, a (click) based hack needs to be put in place.

<div class="btn-group">
    <label 
        class="btn btn-primary" 
        formControlName="privateApplication"
        (click)="form.get('privateApplication').setValue(false)"
        [btnRadio]="false">
            {{'LABEL_PUBLIC' | translate}}
    </label>
    <label 
        class="btn btn-primary" 
        formControlName="privateApplication" 
        (click)="form.get('privateApplication').setValue(true)"
        [btnRadio]="true">
            {{'LABEL_PRIVATE' | translate}}
    </label>
</div>

This is a workaround in order to update the view when using formControl directive.

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 Reactive Form change through ControlValueAccessor doesn’t update view in Angular Angular

One potential solution to this issue is to subscribe to the valueChanges of the form control and then call the setValue method on the control with the emitEvent option set to false. This allows the view to update when the form field value is changed and maintain the expected behavior. The code for this solution is as follows:

control.valueChanges.subscribe(e => {
    control.setValue(e, {emitEvent: false});
});

By subscribing to the valueChanges and setting emitEvent to false, developers can ensure that the view updates when the form field value is changed and maintain the expected behavior.

Another solution is to use Angular’s ngModel directive, which automatically updates the view when changes are made to the form control. This can be done by adding the ngModel directive to the form field and binding it to the form control.

<input [(ngModel)]="control.value">

However, it should be noted that using the ngModel directive will require additional steps to properly implement validation and other form logic. It’s important to find the best solution that fits your use case and requirements.

Other popular problems with Angular

Problem: One common problem faced by Angular developers is poor performance when working with large data sets

This can be caused by the way that Angular’s change detection mechanism works, which can cause the entire component tree to be re-evaluated even when only a small portion of the data has changed.

Solution:

One solution to this problem is to use the OnPush change detection strategy, which only updates a component when its input bindings change or an event is emitted from the component. This can be done by setting the changeDetection property of the component to ChangeDetectionStrategy.OnPush and ensuring that the component’s input bindings are immutable.

@Component({
    selector: 'app-large-dataset',
    templateUrl: './large-dataset.component.html',
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class LargeDatasetComponent {
    @Input() data: any[];
}

Problem: Another common problem faced by Angular developers is dealing with complex forms

This can be caused by the need to handle multiple form controls, validation, and error handling.

Solution:

One solution to this problem is to use Angular’s Reactive Forms, which allows you to create forms in a declarative and reactive way. This can be done by importing the ReactiveFormsModule in your module, creating a FormGroup and FormControl objects, and binding them to the form in your template.

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

@NgModule({
  imports: [ReactiveFormsModule],
  ...
})
export class AppModule { }

// In your component
import { FormGroup, FormControl } from '@angular/forms';

export class FormComponent {
    form = new FormGroup({
        name: new Form
 });

    onSubmit() {
        console.log(this.form.value);
    }
}
<form [formGroup]="form" (ngSubmit)="onSubmit()">
    <input type="text" formControlName="name">
    <input type="email" formControlName="email">
    <input type="password" formControlName="password">
    <input type="password" formControlName="confirmPassword">
    <button type="submit">Submit</button>
</form>

Problem: Another common problem faced by Angular developers is managing the state of the application

This can be caused by the need to share data between different components, and the need to handle side effects such as API calls.

Solution:

One solution to this problem is to use a state management library like NgRx. This can be done by installing the library, creating a store, actions and reducers, and connecting it to your components.

import { StoreModule } from '@ngrx/store';
import { reducers } from './store/app.reducers';

@NgModule({
  imports: [
    StoreModule.forRoot(reducers)
  ]
})
export class AppModule { }
import { Store } from '@ngrx/store';
import { Component } from '@angular/core';
import { Increment, Decrement } from './store/counter.actions';

@Component({
  selector: 'app-root',
  template: `
    <button (click)="increment()">Increment</button>
    <button (click)="decrement()">Decrement</button>
    <p>{{ count$ | async }}</p>
  `
})
export class AppComponent {
  count$ = this.store.select(state => state.counter.count);

  constructor(private store: Store) {}

  increment() {
    this.store.dispatch(new Increment());
  }

  decrement() {
    this.store.dispatch(new Decrement());
  }
}

This way, you can manage the state of your application in a centralized way, making it easier to handle complex data and side effects. Additionally, NgRx also provides tools like the @ngrx/effects library to handle the side effects of your actions in a more organized way. It’s also important to note that this way of organizing the state management is also more scalable and maintainable in the long run as the application grows.

A brief introduction to Angular

Angular is a JavaScript framework for building web applications. It uses a component-based architecture and a declarative approach to programming, making it easy to build and maintain large-scale applications. Angular uses a two-way data binding mechanism, which allows for real-time updates to the view as data changes, and vice versa. This allows for a seamless user experience and efficient handling of user input.

Angular also has a rich set of built-in directives and services, which provide functionality for common tasks such as form validation, routing, and HTTP communication. Additionally, Angular has a powerful template language, which allows for the creation of reusable components and dynamic data binding. Angular also provides a CLI (Command Line Interface) tool which helps to easily create, manage and test angular projects. Angular also supports both unit testing and end-to-end testing out of the box, which helps to ensure the reliability of the application.

Most popular use cases for Angular

  1. Angular can be used to build dynamic, client-side web applications. The framework’s component-based architecture and two-way data binding make it well-suited for creating complex, interactive user interfaces.
  2. Angular can be used to create reusable components and directives, which can be shared across different parts of the application. This allows for efficient code reuse and a more modular design. For example, a code block for a reusable component in Angular might look like this:
import { Component } from '@angular/core';

@Component({
  selector: 'app-example-component',
  template: `<p>This is an example component</p>`
})
export class ExampleComponent { }
  1. Angular can be used to handle routing and navigation within a web application. The framework provides a powerful routing module that allows for easy navigation between different views and components. This can be done by defining the routing path in the app.module.ts file, which will make the routing available throughout the application.
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { HomeComponent } from './home/home.component';
import { AboutComponent } from './about/about.component';

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

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }
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.