typescript - Bygga angular med webpack importerar inte moduler korrekt


Jag har byggt en liten exempelapp för att visa detta problem. Det verkar som att när man importerar moduler i modulteckningen så importeras inte modulens deklarationer. Till exempel, när jag importerar browser-modulen importeras inte NgIf, NgFor osv. och när jag importerar NgSelect importeras ingen av dess scheman eller komponenter. Jag bygger appen utan att använda angular-cli eftersom jag för närvarande migrerar en app från angularjs till angular och att flytta den till att använda angular-cli skulle vara ett problem.

Jag har ställt in webpack-konfigurationen som

const path = require('path'),
AngularWebpackPlugin = require('@ngtools/webpack'),
webpack = require('webpack'),
HtmlWebpackPlugin = require('html-webpack-plugin'),
WebpackDevServer = require('webpack-dev-server');

module.exports = {
    entry: {
        'test': { import: "./app/app.module.ts", filename: 'js/[name].bundle.min.js' },
    },
    mode: 'none',
    output: {
        path: path.resolve(__dirname, "dist"),
        clean: true
    },
    resolve: {
        //extensions: ['.ts', '...'],
        extensions: ['.tsx', '.ts', '.js', '.mjs', '.json']
    },
    optimization: {
    },
    plugins: [
        new webpack.ProvidePlugin({
            moment: 'moment'
        }),
        new HtmlWebpackPlugin({
            title: 'Test',
            template: './app/index.hbs',
            cache: false,
            hash: true,
            inject: false
        }),
        new AngularWebpackPlugin.AngularWebpackPlugin({
            'tsconfig': 'tsconfig.json',
            //"compilerOptions": {},
            "sourceMap": true,
            'jitMode': true
        })
    ],
    module: {
        rules: 
            [
                { 
                    test: /\.hbs$/i, 
                    use: "handlebars-loader",
                    exclude: /node_modules/, 
                },
                {
                    test: /\.[jt]sx?$/,
                    loader: '@ngtools/webpack',
                     include: /app[\\/]/,
                    exclude: [ /node_modules/ ]                
                },
                {
                    test: /\.[cm]?js$/,
                    use: {
                      loader: 'babel-loader',
                      options: {
                        cacheDirectory: true,
                        compact: false,
                        plugins: ['@angular/compiler-cli/linker/babel'],
                      },
                      
                    },
                },
                {
                    test: /\.template\.html$/,
                    exclude: /node_modules/,
                    use: 
                        [
                            {
                                loader: 'raw-loader',
                                options: 
                                {
                                    esModule: false,
                                },
                            }
                        ],   
                }, 
                {
                    test: /\.css$/i,
                    exclude: /node_modules/,
                    use: 
                        [  
                            {
                                loader: 'css-loader',
                                options: {
                                    import: false,
                                    url: false,
                                    modules: false
                                }
                            }    
                        ]
                }
            ],
        },
        devServer: {
            static: {
              directory: path.join(__dirname, 'dist'),
              watch: true
            },
            host: "localhost",
            compress: true,
            port: 5000,
            server: 'https',
            open: true,
            liveReload: false,
            hot: true,
            client: {
                overlay: true,
                progress: true,
            }
          }
}

och det angulära testprogrammet är

app.module.ts

import 'zone.js';

import { NgSelectModule } from '@ng-select/ng-select';
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

@NgModule({
    declarations: [
        AppComponent
    ],
    imports: [
        BrowserModule,
        NgSelectModule,
    ],
    providers: [
    ],
    bootstrap: [AppComponent]
})
export class AppModule {
}

platformBrowserDynamic().bootstrapModule(AppModule)  
  .catch(err => console.error(err));   

app.component.ts

import { Component, Input, OnInit, Output, EventEmitter } from '@angular/core';

@Component({
    selector: 'app-root',
    template: require('./app.template.html'),
})
export class AppComponent implements OnInit {
    public source = [ { id: 1, name: "moo" }]
    public selected = null;

    ngOnInit(): void {
        console.log("YAY!");        
    }
}

app.mall.html

<!DOCTYPE html>
<html lang="en">
<head>
    <title ng-bind="title">T.I.E.R</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta content="IE=edge, chrome=1" http-equiv="X-UA-Compatible" />
</head>
<body>
    <app-root></app-root>
    <script defer src="js/test.bundle.min.js?904b25da0a6807f141ae"></script>
</body></html>

och min tsconfig är:

{
    "compileOnSave": false,
    "compilerOptions": {
      "baseUrl": "./",
      "outDir": "./dist/out-tsc",
      "resolveJsonModule": true,
      "forceConsistentCasingInFileNames": true,
      "strict": false,
      "noImplicitOverride": false,
      "noPropertyAccessFromIndexSignature": false,
      "noImplicitReturns": true,
      "noFallthroughCasesInSwitch": true,
      "sourceMap": true,
      "declaration": true,
      "emitDecoratorMetadata": true,
      "downlevelIteration": true,
      "experimentalDecorators": true,
      "moduleResolution": "Node",
      "importHelpers": true,
      "target": "ES2022",
      "module": "ES2022",
      "useDefineForClassFields": false,
      "lib": [
        "ES2022",
        "dom"
      ]
    },
    "angularCompilerOptions": {
      "enableI18nLegacyMessageIdFormat": false,
      "strictInjectionParameters": true,
      "strictInputAccessModifiers": true,
      "strictTemplates": true,
      "compilationMode": "partial"
    }
  }

Så vitt jag kan se bygger jag Ivy-partialerna, men jag får felen:

NG0304: 'ng-select' är inte ett känt element (använt i 'AppComponent'-komponentens mall):

  1. Om 'ng-select' är en Angular-komponent, kontrollera då att den är en del av ett @NgModule där denna komponent är deklarerad.
  2. Om 'ng-select' är en webbkomponent, lägg då till 'CUSTOM_ELEMENTS_SCHEMA' till '@NgModule.schemas' för denna komponent för att undertrycka detta meddelande.

och

NG0303: Kan inte binda till 'ngModel' eftersom det inte är en känd egenskap hos 'ng-select' (använd i 'AppComponent'-komponentens mall).

  1. Om 'ng-select' är en Angular-komponent och den har inmatningen 'ngModel', kontrollera då att den är en del av en @NgModule där denna komponent är deklarerad.
  2. Om 'ng-select' är en webbkomponent, lägg då till 'CUSTOM_ELEMENTS_SCHEMA' till '@NgModule.schemas' för denna komponent för att undertrycka detta meddelande.
  3. För att tillåta vilken egenskap som helst, lägg till 'NO_ERRORS_SCHEMA' i '@NgModule.schemas' för denna komponent.

som om det inte är importerat. Förutom det fungerar Angular bra. Om jag lägger NgIf i imports kan jag använda det men det borde importeras från BrowserModule eller CommonModule.

Någon som har några idéer eftersom jag har spenderat 3 dagar på det här och är på väg att sticka kniven i min skärm?

Svar #1

Försök att lägga till i din modul app.module.ts schemas: [CUSTOM_ELEMENTS_SCHEMA]

import 'zone.js';

import { NgSelectModule } from '@ng-select/ng-select';
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

@NgModule({
    declarations: [
        AppComponent
    ],
    imports: [
        BrowserModule,
        NgSelectModule,
    ],
    providers: [
    ],
    schemas: [CUSTOM_ELEMENTS_SCHEMA],
    bootstrap: [AppComponent]
})
export class AppModule {
}

platformBrowserDynamic().bootstrapModule(AppModule)  
  .catch(err => console.error(err));   

Svar #2

Du behöver importera CommonModule från @angular/common i modulen där din komponent används. CommonModule tillhandahåller direktiv som *ngIf och *ngFor.

Öppna modul-filen där din komponent är deklarerad, vanligtvis app.module.ts eller en funktionell modul.

Lägg till CommonModule i importarrayen om det inte redan är importerat.

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common'; // Import this to use *ngIf, *ngFor

import { YourComponent } from './your-component.component';

@NgModule({
  declarations: [
    YourComponent
  ],
  imports: [
    CommonModule  // Add this to ensure *ngIf and *ngFor work
  ]
})
export class YourModule { }

Om du använder

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
...
platformBrowserDynamic().bootstrapModule(AppModule)  
  .catch(err => console.error(err));

som LidiyaParshina föreslog, försök då att ställa in din jitMode till @angular/compiler i webpack.config.ts på detta sätt:

        new AngularWebpackPlugin.AngularWebpackPlugin({
            'tsconfig': 'tsconfig.json',
            //"compilerOptions": {},
            "sourceMap": true,
//            'jitMode': true
            'jitMode': '@angular/compiler'
        })

Annars kan du få problem.

För röröversättningar i din app.module.ts bör du skriva:

...
import { CommonModule } from '@angular/common'; // Import this to use *ngIf, *ngFor
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { HttpClientModule, HttpClient } from '@angular/common/http';
import { TranslateModule, TranslateLoader } from '@ngx-translate/core';
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
...
function HttpLoaderFactory(http: HttpClient):TranslateHttpLoader {
  return new TranslateHttpLoader(http, '../i18n/', '.json');
}

...
@NgModule({
  imports: [
    BrowserModule,
    NgbModule,
    HttpClientModule,
    TranslateModule.forRoot({
      loader: {
        provide: TranslateLoader,
        useFactory: HttpLoaderFactory,
        deps: [HttpClient]
      },
    }),
...
  ],
...
})
export class IsbAppModule {}

och i dina funktionsmoduler bör du använda:

...
import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
import { NgSelectModule } from '@ng-select/ng-select';
import { TranslateModule } from '@ngx-translate/core';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
...
@NgModule({
  imports: [
    CommonModule,
    NgbNavModule,
    NgSelectModule,
    NgbModule,
    TranslateModule.forChild(), <--- Put this line
...
  ],
...
})
export class IsbCompanyModule {}

Hoppas det hjälper!