angular universal: dynamic imports for browser only

So there is a way to render PrimeNG components in browser and omit them while server rendering. Those questions helped me start digging the right direction:

angular-cli: Conditional Imports using an environment variable

How can I conditionally import an ES6 module?

While server rendering I used mock component that renders a simple input field and uses the same selector 'p-calendar'. The final code I ended up with in my 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
})

To make your mock component support [(ngModel)] binding use this tutorial. 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;
    }
}


An alternative solution for module imports that does not require dynamic script loading: you can use the compilerOptions.paths option in your server app's tsconfig.json file to redirect the imported module to a server-only version:

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

When the server app builds, the compiler will import the server module instead of the browser module.