web-dev-qa-db-fra.com

Rendu côté serveur avec Angular4 (Angular Universal)

Je travaille sur un projet Webpack Angular4 auquel je voulais ajouter AngularUniversal pour rendre le rendu côté serveur possible. Mais la plupart des tutoriels utilisent cli.Il s'intéresse à Universal avec webpack.J'ai essayé de suivre ceci tutoriel sans chance. Quelqu'un peut-il aider s'il vous plaît.

15
RemyaJ

This Angular Universal est uniquement pour Angular 2. Si vous voulez recommencer à zéro, vous pouvez utiliser ceci Angular 4 Universal Seed qui a toutes les fonctionnalités comme:

  • Angulaire 4
  • WebPack
  • modes dev/prod
  • Compilation SCSS
  • i18n, SEO et TSLint/codelyzer
  • chargement paresseux, config, cache

Ou si vous avez déjà un projet Angular 4 en cours d'exécution, vous pouvez intégrer Universal en définissant les paramètres suivants dans votre code: 

Installez ces paquets: 
npm install @angular/{common,compiler,compiler-cli,core,forms,http,platform-browser,platform-browser-dynamic,platform-server,router,animations}@latest TypeScript@latest --save 

npm install express @types/express --save-dev 

Ajoutez ceci dans votre fichier app.module.ts

import { BrowserModule } from '@angular/platform-browser';
BrowserModule.withServerTransition({
  appId: 'my-app-id'   // withServerTransition is available only in Angular 4
}),

Créer les fichiers suivants 

src/uni/app.server.ts

import { NgModule } from '@angular/core';
import { APP_BASE_HREF } from '@angular/common';
import { ServerModule } from '@angular/platform-server';
import { AppComponent } from '../app/app';
import { AppModule } from '../app/app.module';
import 'reflect-metadata';
import 'zone.js';
@NgModule({
  imports: [
    ServerModule,
    AppModule
  ],
  bootstrap: [
    AppComponent
  ],
  providers: [
    {provide: APP_BASE_HREF, useValue: '/'}
  ]
})
export class AppServerModule {
}


src/uni/server-uni.ts

import 'zone.js/dist/zone-node';
import 'zone.js';
import 'reflect-metadata';
import { enableProdMode } from '@angular/core';
import { AppServerModuleNgFactory } from  '../../aot/src/uni/app.server.ngfactory';
import * as express from 'express';
import { ngUniversalEngine } from './universal-engine';
enableProdMode();
const server = express();
// set our angular engine as the handler for html files, so it will be used to render them.
server.engine('html', ngUniversalEngine({
    bootstrap: [AppServerModuleNgFactory]
}));
// set default view directory
server.set('views', 'src');
// handle requests for routes in the app.  ngExpressEngine does the rendering.
server.get(['/', '/dashboard', '/heroes', '/detail/:id'], (req:any, res:any) => {
    res.render('index.html', {req});
});
// handle requests for static files
server.get(['/*.js', '/*.css'], (req:any, res:any, next:any) => {
    let fileName: string = req.originalUrl;
    console.log(fileName);
    let root = fileName.startsWith('/node_modules/') ? '.' : 'src';
    res.sendFile(fileName, { root: root }, function (err:any) {
        if (err) {
            next(err);
        }
    });
});
// start the server
server.listen(3200, () => {
    console.log('listening on port 3200...');
});

src/uni/universal-engine.ts

import * as fs from 'fs';
import { renderModuleFactory } from '@angular/platform-server';
const templateCache = {}; // cache for page templates
const outputCache = {};   // cache for rendered pages
export function ngUniversalEngine(setupOptions: any) {
  return function (filePath: string, options: { req: Request }, callback: (err: Error, html: string) => void) {
    let url: string = options.req.url;
    let html: string = outputCache[url];
    if (html) {
      // return already-built page for this url
      console.log('from cache: ' + url);
      callback(null, html);
      return;
    }
    console.log('building: ' + url);
    if (!templateCache[filePath]) {
      let file = fs.readFileSync(filePath);
      templateCache[filePath] = file.toString();
    }
    // render the page via angular platform-server
    let appModuleFactory = setupOptions.bootstrap[0];
    renderModuleFactory(appModuleFactory, {
      document: templateCache[filePath],
      url: url
    }).then(str => {
      outputCache[url] = str;
      callback(null, str);
    });
  };
}

Ajoutez la configuration ci-dessous dans votre fichier tsconfig.ts que je suppose situé dans le répertoire racine

{
    "compilerOptions": {
        "baseUrl": "",
        "declaration": false,
        "emitDecoratorMetadata": true,
        "experimentalDecorators": true,
        "lib": ["es2016", "dom"],
        "moduleResolution": "node",
        "outDir": "./dist/out-tsc",
        "sourceMap": true,
        "target": "es5",
        "module": "commonjs",
        "types": ["node"],
        "typeRoots": [
            "node_modules/@types"
        ]
    },
    "files": [
        "src/uni/app.server.ts",
        "src/uni/server-uni.ts"
    ],
    "angularCompilerOptions": {
        "genDir": "aot",
        "entryModule": "./src/app/app.module#AppModule",
        "skipMetadataEmit": true
    },
    "exclude": [
        "test.ts",
        "**/*.spec.ts"
    ]
}

Atlast votre webpack.config.uni.js dans le répertoire racine

const ngtools = require('@ngtools/webpack');
const webpack = require('webpack');
const path = require('path');
const ExtractTextWebpackPlugin = require("extract-text-webpack-plugin");
module.exports = {
    devtool: 'source-map',
    entry: {
        main: ['./src/uni/app.server.ts', './src/uni/server-uni.ts']
    },
    resolve: {
        extensions: ['.ts', '.js']
    },
    target: 'node',
    output: {
        path: path.join(__dirname, "dist"),
        filename: 'server.js'
    },
    plugins: [
        new ngtools.AotPlugin({
            tsConfigPath: './tsconfig.json'
        })
    ],
    module: {
        rules: [
            {
                test: /\.(scss|html|png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)$/,
                use: 'raw-loader'
            },
            { test: /\.ts$/,  loader: require.resolve('@ngtools/webpack') },
            {
                test: /\.(png|jpg|woff|woff2|eot|ttf|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
                loader: 'url?limit=512&&name=[path][name].[ext]?[hash]'
            },
            { test: /\.scss$/, use: [{
                loader: "style-loader" // creates style nodes from JS strings
            }, {
                loader: "css-loader" // translates CSS into CommonJS
            }, {
                loader: "sass-loader" // compiles Sass to CSS
            }] }
        ]
    }
}

Ajoutez les scripts ci-dessous dans votre fichier package.json:

"ngc-build": "ngc -p ./tsconfig.json", // To generate ngFactory file
"build:uni": "webpack --config webpack.config.uni.js",
"serve:uni": "node dist/server.js",

Il y a certaines choses que nous devrions garder à l'esprit:

  • window, document, navigator et d'autres types de navigateurs - n'existent pas sur le serveur - donc leur utilisation, ou toute bibliothèque qui les utilise (jQuery par exemple) ne fonctionnera pas. Vous avez quelques options données dans ce link si vous avez vraiment besoin de certaines de ces fonctionnalités.
13
mohit

Universel angulaire est utilisé uniquement pour 2.x angulaire. Angular 4.x, vous devez utiliser le serveur de la plateforme. Les exemples sont les suivants:

Pour angulaire 2.X.X:

Projet de semences de AngularClass utilisant express/universal

https://github.com/angular/universal-starter

Pour les versions angulaires 4.X.X Utilisez le serveur de plate-forme angulaire

https://github.com/ng-seed/universal

Il y a quelques autres exemples aussi:

5
Deepak Kumar

L'exemple mentionné dans ce didacticiel utilise l'exemple mentionné dans la section Angular Resources. Ils ont récemment mis à jour leur documentation et n'ont pas encore fourni la documentation détaillée pour mettre en œuvre @ angular/universal. This était la page que vous cherchez, mais certains problèmes ont été signalés ici . Peut-être que c'est pourquoi ils l'ont enlevé et ont décidé de le réécrire.

3
Devesh Jadon

Vous pouvez trouver un tutoriel Angular 4 sur le rendu côté serveur avec Webpack sur ce blog .

Caractéristiques:

  • il est construit sur Angular 4
  • cela ne dépend pas de Angular CLI
  • il s'appuie sur Webpack
  • le blog fournit une instruction pas à pas en trois phases:
    • phase 1: Exécution d'une page Hello World renvoyée côté serveur sur un conteneur Docker (je fournis une image Docker préinstallée pour plus de commodité, mais les instructions doivent fonctionner dans votre propre environnement Angular)
    • phase 2: Créer un nouveau lien fonctionnel sur la page principale
    • phase 3 (facultatif): insertion dynamique d'un blog WordPress POST via l'API RESTful

Le résultat final peut être visualisé facilement sur un hôte Docker comme suit:

(dockerhost)$ docker run -it -p 8002:8000 oveits/angular_hello_world:centos bash
(container)# git clone https://github.com/oveits/ng-universal-demo
(container)# cd ng-universal-demo
(container)# npm i
(container)# npm run start

J'ai choisi le port 8002 ci-dessus, car j'utilise déjà d'autres exemples sur les ports 8000 et 8001; Si l'hôte de docker s'exécute sur Virtualbox, vous aurez peut-être besoin d'un mappage de port entre 8002 de l'hôte Virtualbox et 8002 de la machine virtuelle Virtualbox.

Sur un navigateur, accédez à http: // localhost: 8002/blog . Vous verrez le contenu d'un article de blog téléchargé à partir de l'API Wordpress. Avec clic droit-> voir la source, vous verrez le contenu HTML. Cela montre qu'il s'agit d'une page rendue côté serveur.

PS: Comme le didacticiel que vous avez essayé, le didacticiel est basé sur un projet Git créé à l'origine par Rob Wormald , mais avec ce fork de FrozenPandaz , j'ai trouvé une version mise à niveau. to Angular 4 et a mieux fonctionné avec Webpack (voir l’annexe de le blog pour plus de détails).

1
Olli