Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom Library component is not a known element, but app compiles and runs

The Problem

My html files warn that my custom components/pipes 'are not a known element'. However building and running the application works as intended, and the custom components/pipes are functional. The dichotomy of the components working but showing as errors is annoying and can be difficult to work with. I know this is a small thing, but I really want to not see errors in my html files when there are no errors.

Summary

I have two distinct angular projects; an angular library which I consume with my angular application. The library exports several custom components, pipes and services which the application uses. I do not have this library published via npm, instead I use npm-link to give the application access to the library. This seems to work as any service/component defined in my applications components work and doesn't complain. Though the moment I put anything in my template file from my library, that selector is underlined and a warning appears.

The files

/*Library Module*/
@NgModule({
  declarations: [
    ThemeToggleComponent,
    // Other custom components/pipes
  ],
  exports: [
    ThemeToggleComponent,
    // Other components needed in templates files
  ]
})
export class MyCommonModule {}
  
/*
 * Public API Surface of my-common library
 */

export * from './lib/my-common.module';
/*
 * Many other components, services, pipes, etc etc
 */
export * from './lib/theme-toggle/theme-toggle.component';

/* Consuming Application module */
import { MyCommonModule } from 'my-common';

@NgModule({
  imports: [
    MyCommonModule,
  ],
  declarations: [
    MyComponent,
  ],
})
export class MyModule {}
/* Template of MyComponent, my.component.html */
<div>
  <my-theme-toggle (darkThemeSelected)="toggleTheme($event)"></my-theme-toggle>
</div>

The file above will have all lines which reference my custom components underlined in red with the error message 'my-theme-toggle' is not a known element: ...if it is, verify it is apart of the module.

Attempts to fix

I've tried a things, but to no avail

  • Not using NPM link and just copying the library created by ng-packagr into the node modules of my application
  • Rebuilding and relinking/restarting everything
  • Searching for other S.O questions: most questions are about build failures because the import/export was actually incorrect.

My hunches

I think this is an issue with the paths compiler option in tsconfig.json in the consuming application. Throughout all of my research this is the one thing that pops up more than anything else. I currently do not have a path set, because everything is working. My common library looks and acts like any other node_module library, other than this issue.

My other thought is that because everything works and runs, then the issue may be with vs-code not understanding the import, or something of that nature. Though I think this is unlikely.

The background

Both angular projects are on version 9, rc7. I use ng-packagr to build my library. I use visual studio code, and at the time of writing I am on the latest release.

EDIT This problem went away upon upgrading to angular 11, I think several of the other issues might solve it, but this feels like the best option.

like image 607
Zen Avatar asked Sep 05 '25 03:09

Zen


2 Answers

Its An Import Problem, 99.99% of the time!

I've seen this question posted in slightly different ways on different sites, within and out of Stack Overflow. I can tell you its probably not an issue with Angular Language Service.

There are three reasons this can happen

  1. how you've exported the missing Component from the library, e.g. check if you:

    • Added export * from <your-component> to your public-api.ts file
    • Added your Component to the exports of the Library Module that exports it
  2. how you're importing the Library Component, e.g. check if you:

    • linked the library? cd /path/to/<your-library> && npm link
    • linked the application?: cd /path/to/app && npm link <your-library>
    • added the library NgModule(s) YourLibraryModule to imports:[] of your Applications main NgModule
  3. You maybe forgot to declare the parent component in your App NgModule!!

    • This one is huge and not mentioned often. In Angular 10, 11, you can forget to add the Application Component to declarations that references the missing library Component, and the language service / compiler will complain that the Library Component is missing, but not mention the App Component is incorrectly exported!

Why Angular Language Service Restart?

Sometimes the error may change once you restart the language service (e.g. restart VSCode). For scenario 3 I mentioned above, the error becomes:

Error: Cannot determine the module for class YourAppComponent in YourApp/src/app/components/your-app-component/your-app-component.component.ts! Add YourAppComponent to the NgModule to fix it

And voila!! You see the real problem and fixing it is easy!

like image 68
Decoded Avatar answered Sep 10 '25 14:09

Decoded


To fix the issue, you currently have to build with the production flag --prod, because, in its current state, the Angular Language Server doesn't work with Ivy builds. By default the angular prod build builds without Ivy. Thus when it is built, it creates a lib.metadata.json file. This is the file that the language server uses to make the plugin work.

  1. In your library, create a build ng build <lib> --prod
    • add the --watch flag to watch for changes
  2. In your library, create a link cd dist then npm link
    • I created a package.json in the dist folder and created the link there since my lib has multiple modules in it. This will allow you to do
      • import { ModuleAComponent } from '<lib>/moduleA'
      • import { ModuleADirective } from '<lib>/moduleA'
    • Note: The folder you link must have a package.json otherwise it will use the root of the project since that is the closest package.json
    • If you do the same the paths below are in the correct format otherwise they may not be and you can try: "lib": ["./node_modules/lib", "./node_modules/lib/*"]
  3. In your application, link to the package npm link <lib>
  4. In your application, create a path in your tsconfig.base.json (The path might be different depending on how you created your link)
  5. In your application, Restart the Angular Language Server
{
  "compilerOptions": {
    "paths": {
      "lib/*": [
        "./node_modules/lib/*"
      ]
    }
  }
}
like image 21
Get Off My Lawn Avatar answered Sep 10 '25 13:09

Get Off My Lawn