8

是否可以根据条件导入模块?仅当 Angular 2 通用应用程序在浏览器中而不是在服务器中呈现时,才具体导入外部模块。

这个问题与一些依赖浏览器功能并且只能在浏览器中呈现的 PrimeNG 模块有关。在服务器渲染中省略它们会很棒,因为日历和其他组件对 SEO 并不重要。

目前,如果关闭服务器渲染,我可以渲染日历组件。但是,当我在 app.module.ts 中包含以下代码并打开服务器渲染时,服务器会在 button.js 中产生错误“ReferenceError:事件未定义”。

import { CalendarModule } from 'primeng/components/calendar/calendar';
@NgModule({
    ...
    imports: [
        ...,
        CalendarModule
    ]
})

angular 提供了一个 isBrowser 条件。

import { isBrowser } from 'angular2-universal';

但我不知道如何将它用于条件导入。真的有办法为模块做到这一点吗?

4

3 回答 3

9

所以有一种方法可以在浏览器中渲染 PrimeNG 组件并在服务器渲染时省略它们。这些问题帮助我开始挖掘正确的方向:

angular-cli:使用环境变量的条件导入

如何有条件地导入 ES6 模块?

在服务器渲染时,我使用了渲染简单输入字段并使用相同选择器“p-calendar”的模拟组件。我最终在我的 app.module 中得到的最终代码。

...//other imports
import { isBrowser } from 'angular2-universal';

let imports = [
    ... //your modules here
];
let declarations = [
    ... //your declarations here
];

if (isBrowser) {
    let CalendarModule = require('primeng/components/calendar/calendar').CalendarModule;
    imports.push(CalendarModule);
}
else {
    let CalendarMockComponent = require('./components/primeng/calendarmock.component').CalendarMockComponent;
    declarations.push(CalendarMockComponent);
}

@NgModule({
    bootstrap: [AppComponent],
    declarations: declarations,
    providers: [
        ... //your providers here
    ],
    imports: imports
})

要使您的模拟组件支持 [(ngModel)] 绑定,请使用本教程。 http://almerosteyn.com/2016/04/linkup-custom-control-to-ngcontrol-ngmodel

import { Component, forwardRef } from '@angular/core';
import { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms';

export const CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR: any = {
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => CalendarMockComponent),
    multi: true
};

@Component({
    selector: 'p-calendar',
    template: '<input type="text" class="form-control"/>',
    providers: [CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR]
})
export class CalendarMockComponent implements ControlValueAccessor {

    private innerValue: any = '';

    private onTouchedCallback: () => void = () => {};
    private onChangeCallback: (_: any) => void = () => {};

    //From ControlValueAccessor interface
    writeValue(value: any) {
        if (value !== this.innerValue) {
            this.innerValue = value;
        }
    }

    registerOnChange(fn: any) {
        this.onChangeCallback = fn;
    }

    registerOnTouched(fn: any) {
        this.onTouchedCallback = fn;
    }
}

于 2016-11-23T14:28:30.157 回答
4

不需要动态脚本加载的模块导入的替代解决方案:您可以使用compilerOptions.paths服务器应用程序tsconfig.json文件中的选项将导入的模块重定向到仅服务器版本:

{
    ...
    "compilerOptions": {
        ...
        "paths": {
            "path/to/browser/module": "path/to/server/module"
        }
    }
}

当服务器应用程序构建时,编译器将导入服务器模块而不是浏览器模块。

于 2018-03-01T15:36:10.320 回答
-2

我也想回答。这是我的解决方案。您需要创建三个环境,如下所示:

src/environments/server/environments.ts
src/environments/browser/environments.ts
src/environments/environments.ts

然后在angular.json中为浏览器构建部分欺骗文件:

"configurations": {
        "dev": {
          "fileReplacements": [
            {
              "replace": "src/environments/environment.ts",
              "with": "src/environments/browser/environment.ts"
            }
          ]
        },
        "production": {
          ...
          "fileReplacements": [
            {
              "replace": "src/environments/environment.ts",
              "with": "src/environments/browser/environment.ts"
            }
          ]
        }
      }

对于服务器

"configurations": {
        "dev": {
          "fileReplacements": [
            {
              "replace": "src/environments/environment.ts",
              "with": "src/environments/server/environment.ts"
            }
          ]
        },
        "production": {
          ...
          "fileReplacements": [
            {
              "replace": "src/environments/environment.ts",
              "with": "src/environments/server/environment.ts"
            }
          ]
        }
      }

所以我们可以使用变量environment.isServer

import { environment } from '../../environments/environment';
...

environment.isServer ? MapMockModule : MapModule,

如果有不清楚的地方,你可以在这里看到一个例子

于 2019-12-19T14:50:54.063 回答