1

I have a library material-extension inside my angular workspace, in which I'm creating some components I require but Angular Material is lacking. In this extension I've installed the module ngx-editor, which I require in the library components but not in the project itself. ngx-editor seems to also have automatically installed angular itself as one of its dependencies, so I've also ended up with @angular in the node_modules folder of my library.

So I've ended up with this structure:

  • workspace
    • node_modules
      • @angular
      • @angular\material
    • projects
      • angular\material-extension
        • file-upload
        • node_modules
          • @angular
          • ngx-editor
        • text-editor
      • world-builder
        • src

I have worked with angular 14 in the past and am now using angular 18, trying to rewrite some old module based code into the new standalone with signals and injection function. I have one instance where I'm creating a custom material dialog, which uses an injection token according to the Angular Material documentation.

To access the data in your dialog component, you have to use the MAT_DIALOG_DATA injection token:

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

@Component({
  selector: 'your-dialog',
  template: 'passed in {{ data.name }}',
})
export class YourDialog {
  constructor(@Inject(MAT_DIALOG_DATA) public data: {name: string}) { }
}

Which I'm trying to rewrite according to Angular documentation on migration to the inject function.

Before

constructor(
  @Inject(DI_TOKEN) @Optional() readonly token: string) {}
}

After

readonly token = inject(DI_TOKEN, { optional: true });

So in my case particularly, that is from my old

constructor(dialogRef: MatDialogRef<MatTextEditorDialogComponent>,
            @Inject(MAT_DIALOG_DATA) data: any) {
  this.color = 'primary';
  this._dialogRef = dialogRef;
}

to the new

import { Component, inject, input, InputSignal } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { ThemePalette } from '../../../../../../dist/angular/material-extension';

@Component({ template: '' })
export abstract class Dialog {
  public color: InputSignal<ThemePalette> = input<ThemePalette>('primary');
  private _dialogRef: MatDialogRef<Dialog> = inject(MatDialogRef<Dialog>);
  public data: any = inject(MAT_DIALOG_DATA);

  public close(): void {
    this._dialogRef.close();
  }
}

However, I'm facing an error here, saying Argument of type 'InjectionToken<any>' is not assignable to parameter of type 'ProviderToken<any>'., which I assume happens due to the fact that MAT_DIALOG_DATA is imported from the workspace's @angular/material node_module folder (as the library doesn't have angular material installed), while the inject() method is imported from the library's own node_module, which works with the library's instance of @angular/core and thus considers the two incompatible (though I do wonder why the MatDialogRef injection works then...?).

I have worked with Angular's modular approach in the past but am kinda new to the standalone app structure. With modules, each module had its own imports so it made sense to install external modules in library projects (or at least in the library). I'm not sure how to handle this in a standalone app though. Is it even the way to go to have separate node_module folders for a library or should everything just be installed in the workspace? In the former case, how could I tell the library to use the workspace's @angular/core instead of its own? Just remove it from the node_modules folder?

UPDATE:

There was indeed a mismatch between the angular versions in the workspace and the library, the former was @18.1.0 while the latter was @18.2.0.

I've updated the workspace minor version using the command npx npm-check-updates --upgrade --target "minor" --filter "/@angular.*/" I found in this post, which resulted in the following updates:

@angular-devkit/build-angular      ^18.2.7  →  ^18.2.15
@angular/animations                ^18.1.0  →  ^18.2.13
@angular/cdk                       ^18.2.6  →  ^18.2.14
@angular/cli                       ^18.2.0  →  ^18.2.15
@angular/common                    ^18.1.0  →  ^18.2.13
@angular/compiler                  ^18.1.0  →  ^18.2.13
@angular/compiler-cli              ^18.1.0  →  ^18.2.13
@angular/core                      ^18.1.0  →  ^18.2.13
@angular/forms                     ^18.1.0  →  ^18.2.13
@angular/material                  ^18.2.6  →  ^18.2.14
@angular/platform-browser          ^18.1.0  →  ^18.2.13
@angular/platform-browser-dynamic  ^18.1.0  →  ^18.2.13
@angular/platform-server           ^18.1.0  →  ^18.2.13
@angular/router                    ^18.1.0  →  ^18.2.13
@angular/ssr                       ^18.2.7  →  ^18.2.15

With this I was able to ng build my app, but it did not resolve the error. Also, I was now unable to build a different library project, "file-upload" (please see the updated project structure), which doesn't require any additional external modules. The error message I receive is:

✖ Compiling with Angular sources in Ivy partial compilation mode.
Cannot destructure property 'pos' of 'file.referencedFiles[index]' as it is undefined.

I've tried to look for a solution to this but other than a couple different options to try I found here of which none applied to me I couldn't find anything else.

  • I have no "composite": "true" in either the workspace's tsconfig.json nor the project's tsconfig.app.json
  • I have no double relative paths in the tsconfig.json
  • I am not referencing an app component inside the library
  • I am not referencing a file outside of the library

The file-upload library project worked fine until recently (without any changes), though I can't quite say what exactly broke it, as I didn't have to build it for quite a while. As it doesn't require any external module, I've uninstalled everything in the library itself to make sure there's no accidental reference of the library's modules instead of the workspace's one as it should be and reverted any changes to the library's package.json, but this didn't change anything.

I've noticed the package-lock.json didn't seem to have been updated properly (there were still all the 18.1.x Angular modules in there, so I've deleted it and installed it again using the command found in this post:

npm i --package-lock-only

This updated the package-lock.json but I still can't get rid of this Ivy partial compilation mode error.

1 Answer 1

0

You should not define the typing inside the inject that could be the problem.

private _dialogRef: MatDialogRef<Dialog> = inject(MatDialogRefDialog);
Sign up to request clarification or add additional context in comments.

9 Comments

No, that line doesn't throw the error. The following does: public data: any = inject(MAT_DIALOG_DATA); This one, the MAT_DIALOG_DATA is erroneous.
@OttoAbnormalverbraucher please ensure all dependencies have the same angular version, Then make sure you have peer dependencies the same as workspace
@OttoAbnormalverbraucher please share a github repo with the issue happening for debugging if it is possible
I got rid of the partial compilation error. It seems referencing a file from one library entry point in another caused this issue, not just outside of the library. I've then tried installing the same angular version in the library but then for some reason my app tried to reference angular from there and caused another error.
In the end I realized that ngx-editor I'm using in the library doesn't work with server side rendering as it changes the DOM far outside the actual editor element itself, so I would have basically set an exception to most of my application. Thus, I decided to rebuild the app without SSR and at the same time install all external modules in the workspace instead of the library to avoid the issues I had here. But thank you for your effort.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.