Unable to inject NgControl into validator directive in Angular 6

I've encountered a challenge while trying to create a custom validator for a template-driven form. I'm aiming to constructor inject the NgControl of the host element, which is equipped with an NgModel directive. Unfortunately, I'm consistently running into the following Angular compilation error:

ERROR in : Cannot instantiate cyclic dependency! NgModel ("
        <div>
                <label for="name">Your name</label>
                [ERROR ->]<input id="name" type="text" name="name" #name="ngModel" ngModel [requiredIfCondition]="toggleBool" r")

This is how my directive class is structured:

import { Directive, forwardRef, Injector, Input, Self } from '@angular/core';
import { AbstractControl, NG_VALIDATORS, NgControl, Validator } from '@angular/forms';

@Directive({
    selector: '[requiredIf][ngModel]',
    providers: [
        { provide: NG_VALIDATORS, useExisting: forwardRef(() => RequiredIfDirective), multi: true },
    ],
})
export class RequiredIfDirective implements Validator {
    private innerRequiredIfCondition: boolean = false;
    @Input()
    public set requiredIfCondition(val: boolean) {
        this.innerRequiredIfCondition = val;
        let hostControl: AbstractControl | null = this.ngControl.control;
        if (hostControl !== null) {
            hostControl.updateValueAndValidity();
        }
    }

    public constructor(@Self() private readonly ngControl: NgControl) {
    }

    public validate(c: AbstractControl): {[error: string]: string} | null {
        if (this.innerRequiredIfCondition && (c.value === null || c.value === '')) {
            return {
                requiredIf: 'In this context this field is required.',
            };
        }
        return null;
    }
}

When implementing the directive, I'm doing it in this manner:

<div>
    <label for="name">Your name</label>
    <input id="name" type="text" name="name" #name="ngModel" ngModel [requiredIfCondition]="toggleBool" requiredIf />
    <div *ngIf="name.errors && name.errors.requiredIf" class="red-text text-darken-3">
        Name is required now.
    </div>
</div>

I've tried exploring manual injection of NgControl, but due to it being an abstract class, it seems like it's no longer feasible.

Additionally, I attempted to inject AbstractControl instead of NgControl, but encountered a stumbling block as the system couldn't locate a provider for AbstractControl.

The lack of information I could find in relation to template-driven forms has me seeking insights on how to tackle this issue. Any ideas or suggestions would be greatly appreciated.

Thank you, Joshua

Answer №1

While the solution for this issue wasn't found, a workaround was discovered by utilizing the injector.

public constructor(
  private injector: Injector
  @Self() private readonly ngControl: NgControl = this.injector.get(NgControl),
) {}

UPDATE

To resolve this, the provider was removed from the directive itself and provided in the module instead.

Check out the updated code on Stackblitz

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

Error: The AppModule is missing a provider for the Function in the RouterModule, causing a NullInjectorError

https://i.stack.imgur.com/uKKKp.png Lately, I delved into the world of Angular and encountered a perplexing issue in my initial project. The problem arises when I attempt to run the application using ng serve.https://i.stack.imgur.com/H0hEL.png ...

The attribute 'title' cannot be found within the type 'IntrinsicAttributes & IProps'

Hello, I am currently facing an issue where I am trying to hide an item in a different view using interfaces. The specific error I am encountering is mentioned in the title of this query. Let me provide you with part of the code to give you more context: ...

implementing a function to execute after making a successful $http.get request

I have implemented ngrx-store and am attempting to activate a spinner before making an HTTP call, and disabling it once the call has been completed. getInspectionDetails(order) { this.store.dispatch({ type: SPINNER_VISIBLE, payload: true }) //<-- S ...

Does angular have a feature comparable to JavaScript's .querySelectorAll()?

I developed an inventory calculator in JavaScript that provides item count based on weight. The calculator has 4 inputs, and now I'm looking to replicate the same functionality using Angular. Is there a method in Angular similar to .querySelectorAll() ...

Playing noughts and crosses with the minimax strategy

I am struggling with understanding the minimax algorithm and have been working on it for 2 days without success. Can anyone take a look at my code and help me identify any errors? export default class TicTacToeController { /*@ngInject*/ constructor($s ...

Troubleshooting the issue of Angular Reactive Forms Select Option not properly selecting pre-defaulted objects

One issue I am facing is with a select option dropdown that fetches its options from the back-end through an API call and sets them. While trying to have a pre-selected option on page load, setting the value does not seem to be working for me. Even attempt ...

Error: Attempted to call the 'post' method on an undefined object, resulting in an Uncaught TypeError. This occurred within the Ionic 2 framework while executing the Javascript code located

While I attempt to convey my message in English, unfortunately, it is not a language I am fluent in. Currently, I am working on an Ionic 2 project. In this particular scenario, I am trying to make an HTTP request using the POST method while emulating it o ...

Dealing with NPM problems during Angular 9.0.7 setup issues

I encountered a problem after a recent Windows update that corrupted my system. I had to reinstall Windows, and now I am unable to run my Angular project, which was originally in version 9.0.7 with a package.json file. I tried installing Angular 9.0.7 glob ...

Avoid the occurrence of the parent's event on the child node

Attempting to make changes to an existing table created in react, the table is comprised of rows and cells structured as follows: <Table> <Row onClick={rowClickHandler}> <Cell onCLick={cellClickHandler} /> <Cell /> ...

What is the process for applying cdkDropList to the tbody when using mat-table instead of a traditional HTML table?

I have been experimenting with the mat-table component in my Angular project, following a simple example from the documentation: <table mat-table [dataSource]="dataSource" class="mat-elevation-z8"> <!--- These columns can be ...

Is there a way to automatically close the previous accordion when scrolling to a new one on the page?

Currently, I am working with the material-ui accordion component and facing an issue where all accordions are initially opened. As I scroll down the page and reach a new accordion, I want the previous ones to automatically close. The problem arises when tr ...

Running headless Chrome with Protractor on Windows platform is presenting difficulties

While there is a wealth of documentation available on headless chrome automated testing, information specifically for Windows users seems to be lacking. Furthermore, details on utilizing headless chrome for end-to-end automated testing in a fully develope ...

Issue encountered: The .scss loader did not yield a string following the upgrade to cli version 7.3.6

After using ng update to upgrade my project, I encountered the following error when running ng build --prod: ERROR in The loader "...\ClientApp\src\app\nav-menu\nav-menu.component.scss" didn't return a string. Here is a li ...

What is the best way to simplify passing repeated children properties while ensuring non-optional types are maintained?

One of my components is being used multiple times consecutively with some properties being repeated and some being unique: interface InsideComponentProps { repeatedThing: string; uniqueThing: string; } const InsideComponent: React.SFC<InsideCo ...

Issue with BehaviorSubject<Object[]> causing incorrect array data upon initial subscription

I am facing an issue with a BehaviorSubject where the first .subscribe callback is returning an Array with 6 Objects. Strangely, in console output, it shows length: 6, but every for-loop I iterate through the array only runs 5 times and even when I log arr ...

Implementing basic authentication and Cross-Origin Resource Sharing (CORS) in

I am currently attempting to make a simple HTTP API call using Angular to a standard API with Basic HTTP Authentication. However, I am encountering an issue where the browser is blocking the request with a "Cross-Origin Request Blocked" error message, citi ...

A comprehensive guide to dynamically rendering components and implementing dynamic CSS in Angular 6

I successfully implemented a Directive, but I am now facing the challenge of adding CSS styling to it. I have experimented with array objects, however, I ended up applying CSS directly in the HTML. My goal is to create a single component where I can pass ...

The theming feature in Angular 5 with Bootstrap 4 and Bootswatch seems to be malfunctioning

Having trouble implementing bootswatch themes with angular 5 and bootstrap 4? I've added the following to styles.scss: @import "~bootswatch/dist/cerulean/variables"; @import "~bootstrap/scss/bootstrap"; @import "~bootswatch/dist/cerulean/ ...

Retrieve the name of the Form Control

Currently, I am working on Angular reactive forms where I have the task of comparing the start date and end date controls. Both dates are validated using the function "dateLessThan", but I am struggling with determining which control is being evaluated. / ...

Exploring Nested <Routes> with ReactJs

My decision on whether to display the Foo page or the Bar page is based on the route. However, the Foo page itself contains two sub-routes for components to render depending on the URL path - such as FooOne or FooTwo. This results in having two layers of r ...