How to globally import some module in Angular with Standalone Components?
Asked Answered
N

2

6

I've some modules that I want to globally use everywhere in my app, so I don't want to have to declare them in every components.

Currently, I've to do the following:

@Component({
  selector: 'app-blank',
  templateUrl: './blank.component.html',
  imports: [RouterOutlet, MaterialModule, CommonModule],
  standalone: true,
  styleUrls: [],
})

I would like to avoid having to declare the MaterialModule and CommonModule.

I tried to remove them from the import list and in my app.config.ts:

export const appConfig: ApplicationConfig = {
  providers: [
    provideRouter(routes),
    provideAnimationsAsync(),
    importProvidersFrom(
      CommonModule, //<-- this
      MaterialModule,//<-- and this
      TablerIconsModule.pick(TablerIcons),
      FeatherModule.pick(allIcons)
    ),
  ],
};

This app.config.ts is referenced in my main.ts:

import { appConfig } from './app/app.config';

bootstrapApplication(AppComponent, appConfig).catch((err) =>
  console.error(err)
);

But when it runs, I get this in the compiler(which I don't if I have those 2 modules in my import):

X [ERROR] NG8001: 'mat-sidenav-container' is not a known element:                                                                                       
1. If 'mat-sidenav-container' is an Angular component, then verify that it is included in the '@Component.imports' of this component.
2. If 'mat-sidenav-container' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@Component.schemas' of this component to suppress this message. [plugin angular-compiler]

    src/app/layouts/blank/blank.component.html:4:0:
      4 │ <mat-sidenav-container
        ╵ ~~~~~~~~~~~~~~~~~~~~~~

  Error occurs in the template of component BlankComponent.

    src/app/layouts/blank/blank.component.ts:8:15:
      8 │   templateUrl: './blank.component.html',
        ╵                ~~~~~~~~~~~~~~~~~~~~~~~~


X [ERROR] NG8002: Can't bind to 'ngClass' since it isn't a known property of 'mat-sidenav-container'.
1. If 'mat-sidenav-container' is an Angular component and it has 'ngClass' input, then verify that it is included in the '@Component.imports' of this component.
2. If 'mat-sidenav-container' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@Component.schemas' of this component to suppress this message.
3. To allow any property add 'NO_ERRORS_SCHEMA' to the '@Component.schemas' of this component. [plugin angular-compiler]

    src/app/layouts/blank/blank.component.html:5:2:
      5 │   [ngClass]="{
        ╵   ~~~~~~~~~~~~

  Error occurs in the template of component BlankComponent.

    src/app/layouts/blank/blank.component.ts:8:15:
      8 │   templateUrl: './blank.component.html',

It's an app that I just migrated from angular 16 to angular 17, I'm not sure if there is something more to do? What am I missing?

Here is my angular.json, since I had to modify it to use vite and the new builder:

{
  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
  "version": 1,
  "newProjectRoot": "projects",
  "projects": {
    "flexy": {
      "projectType": "application",
      "schematics": {
        "@schematics/angular:component": {
          "style": "scss"
        }
      },
      "root": "",
      "sourceRoot": "src",
      "prefix": "app",
      "architect": {
        "build": {
          "builder": "@angular-devkit/build-angular:application",
          "options": {
            "allowedCommonJsDependencies": [
              "@swimlane/ngx-charts",
              "ng-apexcharts",
              "ngx-scrollbar",
              "rfdc",
              "apexcharts",
              "bezier-easing",
              "c3"
            ],
            "outputPath": "dist/flexy",
            "browser": "src/main.ts",
            "index": "src/index.html",
            "polyfills": ["zone.js"],
            "tsConfig": "tsconfig.app.json",
            "inlineStyleLanguage": "scss",
            "assets": ["src/favicon.ico", "src/assets"],
            "styles": [
              "src/styles.scss",
              "src/assets/scss/style.scss"
            ],
            "scripts": []
          },
          "configurations": {
            "production": {
              "budgets": [
                {
                  "type": "initial",
                  "maximumWarning": "12mb",
                  "maximumError": "12mb"
                },
                {
                  "type": "anyComponentStyle",
                  "maximumWarning": "12mb",
                  "maximumError": "12mb"
                }
              ],
              "outputHashing": "all"
            },
            "development": {
              "optimization": false,
              "extractLicenses": false,
              "sourceMap": true,
              "namedChunks": true
            }
          },
          "defaultConfiguration": "production"
        },
        "serve": {
          "builder": "@angular-devkit/build-angular:dev-server",
          "configurations": {
            "production": {
              "buildTarget": "flexy:build:production"
            },
            "development": {
              "buildTarget": "flexy:build:development"
            }
          },
          "defaultConfiguration": "development"
        },
        "extract-i18n": {
          "builder": "@angular-devkit/build-angular:extract-i18n",
          "options": {
            "buildTarget": "flexy:build"
          }
        },
        "test": {
          "builder": "@angular-devkit/build-angular:karma",
          "options": {
            "polyfills": ["zone.js", "zone.js/testing"],
            "tsConfig": "tsconfig.spec.json",
            "inlineStyleLanguage": "scss",
            "assets": ["src/favicon.ico", "src/assets"],
            "styles": ["src/styles.scss"],
            "scripts": []
          }
        }
      }
    }
  },
  "cli": {
    "analytics": false
  }
}

Seeing the first answers, I might have not been clear enough. The question is not about how to import specific component in my standalone components(like the RouterOutlet), but how to import "globally" for all my standalone components, a few modules containing all the components I want to import everywhere.

Nonviolence answered 8/3 at 16:39 Comment(3)
#76707024 look at Selaka Nanayakkara answer i will try to make a my common module and import CommonModule, MaterialModule importProvidersFrom( myCommonModule ),Lyric
@salhimustapha hi, thanks for the answer, but I'm looking for a way to import this module globally for all my components, like we would have done in the app module previouslyNonviolence
You may visit the link, hope it will be helpful. #77927199Nevis
P
1

If you have @angular/material migrated as well you can import standalone components from it:

import { NgIf } from '@angular/common';
import { Component } from '@angular/core';
import {
  MatSidenav,
  MatSidenavContainer,
  MatSidenavContent,
} from '@angular/material/sidenav';

@Component({
  selector: 'app-test',
  standalone: true,
  template: `
    <mat-sidenav-container *ngIf="showSidenav" class="example-container">
      <mat-sidenav mode="side" opened>Sidenav content</mat-sidenav>
      <mat-sidenav-content>Main content</mat-sidenav-content>
    </mat-sidenav-container>`,
  imports: [NgIf, MatSidenav, MatSidenavContainer, MatSidenavContent],
})
export class TestComponent {
  showSidenav = true;
}

The same for CommonModule members: *ngIf is a directive from it and this directive is standalone one which means you can import it on its own.

stackblitz@demo

Postulant answered 8/3 at 17:34 Comment(5)
Hi, thank you for your answer, but that's not what I'm asking. There are some components that I will use everywhere, so for those common components, I would like to regroup them in a module a import them only once for my whole appNonviolence
As far as I know it is imposible to do that in Angular. You cannot import components/directives once, globally. They are always imported per module/component.Postulant
@Nonviolence Yeah, don't load modules which aren't needed in the component. If you're not using angular material components in your AppComponent then you shouldn't import them there, nor globally. It increases the size of your javascript bundles unnecessarily, and visitors need to download tons of code they don't need. Google configured warning/error bundle size limits for a reason. Checkout the webpack-bundle-analyzer, it shows what code lives in your main bundle and you should get rid of unnecessary chunks/modules there.Reciprocation
You may also want to check lazy-loading aka module-per-page. Then you can use PreloadAllModules which download the rest of your application after your application has done loadingReciprocation
@Reciprocation Those are the modules that get used in most of my components (like labels, titles, textboxes, skeletons, ...) And I've no page where I don't have at least one instance of them. But again, I'm not asking if it's a good idea, just how to do it. It has been done like this for 16 versions of angular and working fine.Nonviolence
L
0

I'm facing a similar need and I think a workaround for this scenario is to create and export a global constant containing a reference to all the modules you want to reuse. Then you can use the spread operator [...globalModules, SpecificModule1] in whatever standalone component you want.

global_modules.ts

import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
import { TranslocoModule } from '@jsverse/transloco';

export const globalModules = [
  // Globally loaded modules
  CommonModule,
  RouterModule,
  FontAwesomeModule,
  TranslocoModule,
]

Component

import { globalModules } from 'src/app/global_modules';
// Other imports...

@Component({
  standalone: true,
  imports: [
    ...globalModules,

    // Pipes
    DateAgoPipe,
  ],
  selector: 'app-comment',
  templateUrl: './comment.component.html'
})
export class CommentComponent {
  // Your class body
}
Lactescent answered 7/6 at 22:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.