Is Angular CLI incorrectly flagging circular dependencies for nested Material Dialogs?

My Angular 8 project incorporates a service class that manages the creation of dialog components using Angular Material. These dialogs are based on different component types, and the service class is designed to handle their rendering. Below is a simplified version of this service:

@Injectable()
export class MyService {
    renderDialogTypeOne() {
        // method implementation that uses matDialog.open(TypeOne)
    }

    renderDialogTypeTwo() {
        // method implementation that uses matDialog.open(TypeTwo)
    }
}

The challenge arises from the fact that the service class references the types it renders, resulting in a dependency. However, one of the rendered types (TypeTwo) contains an instance of the aforementioned service class within its constructor. This is necessary for TypeTwo to launch its own dialogs of TypeOne:

export class TypeOne {
}

export class TypeTwo {
    contructor(private service: MyService) { }

    showNestedDialog() {
        this.service.renderDialogTypeOne();
    }
}

This situation can be perceived as a circular dependency between the service class and TypeTwo. While splitting the service class into multiple parts could potentially resolve this issue, it might not be ideal to do so solely to address a compiler warning.

Does this truly constitute a circular dependency? If so, isn't this a common occurrence where two entities have a sort of chicken-and-egg relationship?

Are there any feasible solutions other than simply turning off Angular's circular dependency notifications?

Answer №1

The code snippet from the Angular Material repository related to the Dialog component reveals the use of an Injector for creating the Component that appears within the Dialog window. This method helps in avoiding circular dependencies as discussed on Stack Overflow.

Consequently, it seems that the warnings about potential circular dependencies are not accurate.

If needed, one can opt to disable these circular dependency warnings globally by tweaking the angular.json configuration file. Unfortunately, this adjustment cannot be applied at a per-file level.

angular.json

....
  "defaults": {
    ....
    "build": {
      "showCircularDependencies": false
    }
  }

Workaround

To address scenarios where nested calls might occur, consider using the following approach. For instance, a Dialog belonging to type DialogYesNoComponent could launch another Dialog of type DialogWarningComponent, and vice versa.

Example

import { DialogService, DialogYesNoComponent, DialogWarningComponent } from '...'


export class TypeOne {
  constructor(private dialog_service: DialogService) { }

  // Method to prompt a yes/no dialog
  showYesNoDialog() {
    const dialog_question = "Would you like to continue?";
    const dialog_ref: MatDialogRef<DialogYesNoComponent> =
      this.dialog_service.open_yes_no_dialog({
        question: dialog_question,
        title: 'Confirm', height: '300px' })
    dialog_ref.afterClosed().subscribe(
      (choice: 'yes' | 'no') => {
        if (choice === 'yes') {
          // Continue with program logic
        } else {
          // Open Nested Warning Dialog
          this.showWarningDialog("Stopping the program.");
        }
      }
    )
  }

  // Display warning dialog
  showWarningDialog(warning: String) {
    ...
  }
}

DialogService

import { ElementRef, Injectable } from '@angular/core';
import { MatDialog, MatDialogRef } from '@angular/material';

import { DialogWarningComponent } from './dialog-warning/dialog-warning.component';
import { DialogYesNoComponent } from './dialog-yes-no/dialog-yes-no.component';

@Injectable()
export class DialogService {
  constructor(public dialog: MatDialog) { }

  // Method to open a yes/no type dialog
  public open_yes_no_dialog({ question, title = 'Confirm', yes_button_first = true,
    has_backdrop = false, height = '250px', width = '350px' }:
    {
      question: string, title?: string, yes_button_first?: boolean, has_backdrop?: boolean,
      height?: string, width?: string
    }): MatDialogRef<DialogYesNoComponent> {

    const dialog_ref = this.dialog.open(DialogYesNoComponent, {
      autoFocus: true,
      backdropClass: 'cdk-overlay-transparent-backdrop',
      closeOnNavigation: true,
      disableClose: false,
      hasBackdrop: has_backdrop,
      height: height,
      width: width,
      data: { question: question, title: title, yes_button_first: yes_button_first }
    })

    return dialog_ref
  }

  // Method to display warning dialog
  public open_warning_dialog() {
    { warning, title = 'Warning',
    has_backdrop = false, height = '250px', width = '350px' }:
    {
      warning: string, title?: string, has_backdrop?: boolean,
      height?: string, width?: string
    }): MatDialogRef<DialogWarningComponent> {

    const dialog_ref = this.dialog.open(DialogWarningComponent, {
      autoFocus: true,
      backdropClass: 'cdk-overlay-transparent-backdrop',
      closeOnNavigation: true,
      disableClose: false,
      hasBackdrop: has_backdrop,
      height: height,
      width: width,
      data: { warning: warning, title: title }
    })

    return dialog_ref
  }
}

DialogYesNoComponent

import { Component, Inject } from '@angular/core';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';


export interface YesNoDialogOptions {
  question: string
  title: string
  yes_button_first: boolean
}


@Component({
  selector: 'dialog-yes-no',
  templateUrl: './dialog-yes-no.component.html',
  styleUrls: ['./dialog-yes-no.component.css']
})
export class DialogYesNoComponent {
  constructor(public dialog_ref: MatDialogRef<DialogYesNoComponent>,
    @Inject(MAT_DIALOG_DATA) public options: YesNoDialogOptions) { }
}

DialogWarningComponent

import { Component, Inject } from '@angular/core';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';


export interface WarningDialogOptions {
  warning: string
  title: string
}


@Component({
  selector: 'dialog-warning',
  templateUrl: './dialog-warning.component.html',
  styleUrls: ['./dialog-warning.component.css']
})
export class DialogWarningComponent {
  constructor(public dialog_ref: MatDialogRef<DialogWarningComponent>,
    @Inject(MAT_DIALOG_DATA) public options: WarningDialogOptions) { }
}

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

Issue encountered while trying to run `npm install` on an angular-cli

I recently moved my angular-cli project with node modules to a new directory. Upon running npm install, I encountered the following warnings: npm WARN deprecated <a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="e2838c85978e8 ...

return to the original secured page based on the most recent language preference

I'm facing an issue with a logical redirection that needs to redirect users to the previous protected page after login. The login functionality is implemented using my custom login page and Google Credentials. Additionally, I have set up a multilingu ...

Understanding the correct way to map two arrays with boolean values is essential for effective data

My situation involves two lists: accounts and accountsWithSelectedField. I initially mapped accountsWithSelectedField like this: this.accountsWithSelectedField = this.accounts.map(s => ({...s, selected: false})); Subsequently, upon receiving a list of ...

Instructions on invoking a setter method in Angular

I am looking to implement a setter method to update my form. When should I invoke the setter method? Below is the code snippet from my component.ts: export class UpdateZoneComponent implements OnInit { profileForm: FormGroup products: any; h: number; c ...

What is the process for adjusting the color of a mat-divider?

Has anyone been successful in changing the color of mat-divider? I attempted the following but had no luck: component.html <mat-divider class="material-devider"></mat-divider> component.scss .material-devider { color: red } ...

Access exclusive content by subscribing now!

How can I return a reference to a subject from a service without allowing the receiver to call .next() on the subject? Let's say there is a service with a subject that triggers new events. class ExampleService { private exampleSubject = new Subjec ...

Struggling to comprehend the intricacies of these generic declarations, particularly when it comes to Type Argument Lists

I'm currently reviewing the code snippet from the TypeScript definitions of fastify. I am struggling to understand these definitions. Although I am familiar with angle brackets used for generics, most TypeScript tutorials focus on simple types like Ar ...

How can a parent's method be activated only after receiving an event emitter from the child and ensuring that the parent's ngIf condition is met?

Every time the element in the child template is clicked, it triggers the method activateService(name) and emits an event with the name of the selected service using the event emitter serviceSelected. The parent component's method scrollDown(name) is t ...

Update the component following an HTTP post request

I have an addProjectModal component that allows users to add new projects. save(data:Project) { data.customer_id = this.customerID; data.supervisor_id = 450; this._projectService.addProject(data) .subscribe(res => console.log(res)); //initiat ...

Discover the steps to implement user authentication with a combination of username, password, and token in an Angular 4

After developing a form using Angular 4, I encountered the need to send the form data via the post method with Angular 4. Testing with Postman showed that the data is being received correctly. To accomplish this, I must use Basic Auth with a username and p ...

Dealing with Errors in Angular 2/4 using forkJoin for Multiple URLs

I am currently using Angular 2 and implementing forkJoin for a particular scenario where I need to make multiple REST calls in parallel. Below is the function I am using: private getBatchObservableData(data: Array<Widget>): Observable<any> { ...

Potential uncertainty in Angular FormControl email validation due to potential null Object

Whenever I run the command ng s --aot, I encounter this message: Object is possibly 'null'. I've been trying various solutions all morning to resolve it, but without success. The issue seems to be related to objects like email.valid, dirty, ...

Guidelines for Managing Test Cases for a Promise-Returning Function with Resolve and Reject in Angular 6

I need to write a test case for a function that returns a Promise with resolve and reject. Here's the function definition: isAuthSuccess(): Promise<any> { const promise = new Promise((resolve, reject) => { if (this.userInfo) { ...

Typescript libraries built specifically for unique custom classes

I am currently exploring the most effective method for creating a class library in Typescript and deploying it to NPM along with a definitions file. The classes within the library serve as models that are utilized by multiple RESTful services. Some of the ...

Is there a way to turn off tsc pretty printing using the configuration file?

My typescript program is intentionally broken but I want to fix it. 12:17:23:~/hello $ cat hello.ts console.log("Hello World" 12:17:29:~/hello $ cat package.json { "dependencies": { "typescript": "^5.2.2" ...

Interfaces and Accessor Methods

Here is my code snippet: interface ICar { brand():string; brand(brand:string):void; } class Car implements ICar { private _brand: string; get brand():string { return this._brand; } set brand(brand:string) { this. ...

Looking to include a badge within the nebular menu

Can someone assist me with adding a badge to the Nebular menu to display the inbox count dynamically? Any help would be greatly appreciated. Thanks! import { NbMenuItem } from '@nebular/theme'; export const MENU_ITEMS: NbMenuItem[] = [ { ti ...

Navigate to an Angular page URL using a Spring Boot REST API

I've been trying to figure out how to redirect to an Angular page from a Spring Boot API, but so far none of the methods I've tried have worked. Front end: (Angular) http://localhost:4200 Back end: (Spring Boot) http://localhost:80 ...

Angular: Utilize a Pipe in Template to Format and Display Time With a Mask

Within my code, there's a function called secondsToHms that serves to convert seconds into hours, minutes, and seconds: transform(seconds: number): string { const minutes = Math.floor(seconds / 60); //minutes const secs = seconds % 60; //seconds ...

A Guide to Importing Modules in Angular by Assigning Paths to Variables

I am facing an issue with importing my angular module using a variable as the path. For example: When I directly use this code, everything works fine: import('src/app/modules/public/public.module').then(mod => mod.PublicModule); However, wh ...