angular / universal-starter

Angular Universal starter kit by @AngularClass
2.02k stars 688 forks source link

Data not shown in view source #630

Open renilbabu03 opened 6 years ago

renilbabu03 commented 6 years ago

I have implemented Angular Universal for Angular 5. Everything has compiled well enough. The only places where I see the universal work ( html with content in view source) is in static pages i.e where no data is fetched from the server. Views having data fetched from resolve or any other means is not shown in the view source of the page. I am not quite sure what I am missing. Can you please help.

Below is the screen shot of the view source of page having data from server. issue

Can anyone look into this issue. Have been stuck in this for past week. No results.

Below are my files .

### 1. angular-cli.json

{
   "$schema":"./node_modules/@angular/cli/lib/config/schema.json",
   "project":{
      "name":"project"
   },
   "apps":[
      {
         "root":"src",
         "outDir":"dist/browser",
         "assets":[
            "assets",
            "favicon.ico"
         ],
         "index":"index.html",
         "main":"main.ts",
         "polyfills":"polyfills.ts",
         "test":"test.ts",
         "tsconfig":"tsconfig.app.json",
         "testTsconfig":"tsconfig.spec.json",
         "prefix":"app",
         "styles":[

         ],
         "scripts":[

         ],
         "environmentSource":"environments/environment.ts",
         "environments":{
            "dev":"environments/environment.ts",
            "stag":"environments/environment.stag.ts",
            "prod":"environments/environment.prod.ts"
         }
      },
      {
         "platform":"server",
         "root":"src",
         "outDir":"dist/server",
         "assets":[
            "assets",
            "favicon.ico"
         ],
         "index":"index.html",
         "main":"main.server.ts",
         "test":"test.ts",
         "tsconfig":"tsconfig.server.json",
         "testTsconfig":"tsconfig.spec.json",
         "prefix":"app",
         "styles":[
            "styles.css"
         ],
         "scripts":[

         ],
         "environmentSource":"environments/environment.ts",
         "environments":{
            "dev":"environments/environment.ts",
            "stag":"environments/environment.stag.ts",
            "prod":"environments/environment.prod.ts"
         }
      }
   ]
}

### 2. server.ts

import "zone.js/dist/zone-node";
import "reflect-metadata";
import {
    renderModuleFactory
} from "@angular/platform-server";
import {
    enableProdMode
} from "@angular/core";

import * as express from "express";
import {
    join
} from "path";
import {
    readFileSync
} from "fs";

// Faster server renders w/ Prod mode (dev mode never needed)
enableProdMode();

// Express server
const app = express();

const PORT = process.env.PORT || 4000;
const DIST_FOLDER = join(process.cwd(), "dist");

// Our index.html we'll use as our template
const template = readFileSync(
    join(DIST_FOLDER, "browser", "index.html")
).toString();

// * NOTE :: leave this as require() since this file is built Dynamically from webpack
const {
    AppServerModuleNgFactory,
    LAZY_MODULE_MAP
} = require("./dist/server/main.bundle");

// Express Engine
import {
    ngExpressEngine
} from "@nguniversal/express-engine";
// Import module map for lazy loading
import {
    provideModuleMap
} from "@nguniversal/module-map-ngfactory-loader";

// Our Universal express-engine (found @ https://github.com/angular/universal/tree/master/modules/express-engine)
app.engine(
    "html",
    ngExpressEngine({
        bootstrap: AppServerModuleNgFactory,
        providers: [provideModuleMap(LAZY_MODULE_MAP)]
    })
);

app.set("view engine", "html");
app.set("views", join(DIST_FOLDER, "browser"));

/* - Example Express Rest API endpoints -
  app.get('/api/**', (req, res) => { });
*/

// Server static files from /browser
app.get(
    "*.*",
    express.static(join(DIST_FOLDER, "browser"), {
        maxAge: "1y"
    })
);

// ALl regular routes use the Universal engine
app.get("*", (req, res) => {
    res.render("index", {
        req
    });
});

// Start up the Node server
app.listen(PORT, () => {
    console.log(`Node Express server listening on http://localhost:${PORT}`);
});

### 3. prerender.ts

// Load zone.js for the server.
import "zone.js/dist/zone-node";
import "reflect-metadata";
import {
    readFileSync,
    writeFileSync,
    existsSync,
    mkdirSync
} from "fs";
import {
    join
} from "path";
import {
    chdir
} from "process";

import {
    enableProdMode
} from "@angular/core";
const domino = require("domino");
const fs = require("fs");
const path = require("path");
const templateA = fs
    .readFileSync(path.join("..", "dist/browser", "index.html"))
    .toString();
const win = domino.createWindow(templateA);
win.Object = Object;
win.Math = Math;

global["window"] = win;
global["document"] = win.document;
global["branch"] = null;
global["object"] = win.object;
// Faster server renders w/ Prod mode (dev mode never needed)
enableProdMode();

// Express Engine
import {
    ngExpressEngine
} from "@nguniversal/express-engine";
// Import module map for lazy loading
import {
    provideModuleMap
} from "@nguniversal/module-map-ngfactory-loader";

import {
    renderModuleFactory
} from "@angular/platform-server";

// * NOTE :: leave this as require() since this file is built Dynamically from webpack
const {
    AppServerModuleNgFactory,
    LAZY_MODULE_MAP
} = require("./dist/server/main.bundle");

// Get route paths to prerender only static pages
const PATHS = require("./static.paths");

const BROWSER_FOLDER = join(process.cwd(), "browser");

// Load the index.html file containing referances to your application bundle.
const index = readFileSync(join("browser", "index.html"), "utf8");

let prom = Promise.resolve();

// Iterate each route path
PATHS.forEach(function(route) {
    // Changes current directory to ./dist/browser
    chdir(BROWSER_FOLDER);

    // Creates new directories (if not exists) and changes current directory for the nested one
    route
        .split("/")
        .filter(val => val !== "")
        .forEach(function(dir) {
            if(!existsSync(dir)) {
                mkdirSync(dir);
            }
            chdir(dir);
        });

    // Writes rendered HTML to index.html, replacing the file if it already exists.
    prom = prom
        .then(_ =>
            renderModuleFactory(AppServerModuleNgFactory, {
                document: index,
                url: route,
                extraProviders: [
                    [provideModuleMap(LAZY_MODULE_MAP)]
                ]
            })
        )
        .then(html =>
            writeFileSync(join(BROWSER_FOLDER, route, "index.html"), html)
        );
});

### 4. package.json

{
   "name":"sirved",
   "version":"0.0.0",
   "license":"MIT",
   "scripts":{
      "ng":"ng",
      "start":"ng serve",
      "build":"ng build --prod",
      "test":"ng test",
      "lint":"ng lint",
      "e2e":"ng e2e",
      "build:client-and-server-bundles":"ng build --prod && ng build --prod --app 1 --output-hashing=false",
      "build:static":"npm run build:client-and-server-bundles && npm run webpack:server && npm run generate:static",
      "build:dynamic":"npm run build:client-and-server-bundles && npm run webpack:server",
      "generate:static":"cd dist && node prerender",
      "webpack:server":"webpack --config webpack.server.config.js --progress --colors",
      "serve:static":"cd dist/browser && http-server",
      "serve:dynamic":"node dist/server"
   },
   "private":true,
   "dependencies":{
      "@agm/core":"^1.0.0-beta.3",
      "@angular/animations":"^5.2.11",
      "@angular/common":"^5.2.0",
      "@angular/compiler":"^5.2.0",
      "@angular/core":"^5.2.0",
      "@angular/forms":"^5.2.0",
      "@angular/http":"^5.2.0",
      "@angular/platform-browser":"^5.2.0",
      "@angular/platform-browser-dynamic":"^5.2.0",
      "@angular/platform-server":"^5.2.0",
      "@angular/router":"^5.2.0",
      "@nguniversal/express-engine":"^5.0.0-beta.1",
      "@nguniversal/module-map-ngfactory-loader":"^5.0.0-beta.1",
      "@types/jquery":"^3.3.4",
      "core-js":"^2.4.1",
      "zone.js":"^0.8.19"
   },
   "devDependencies":{
      "@angular/cli":"~1.7.4",
      "@angular/compiler-cli":"^5.2.0",
      "@angular/language-service":"^5.2.0",
      "@types/googlemaps":"^3.30.11",
      "@types/jasmine":"~2.8.3",
      "@types/jasminewd2":"~2.0.2",
      "@types/node":"~6.0.60",
      "codelyzer":"^4.0.1",
      "jasmine-core":"~2.8.0",
      "jasmine-spec-reporter":"~4.2.1",
      "karma":"~2.0.0",
      "karma-chrome-launcher":"~2.2.0",
      "karma-coverage-istanbul-reporter":"^1.2.1",
      "karma-jasmine":"~1.1.0",
      "karma-jasmine-html-reporter":"^0.2.2",
      "protractor":"~5.1.2",
      "ts-node":"~4.1.0",
      "tslint":"~5.9.1",
      "typescript":"~2.5.3",
      "ts-loader":"^2.3.7"
   }
}

I have done exactly same as the angular universal starter project is done but I am not getting the required results. Could anyone please look into this!

jandeu commented 5 years ago

I've had a similar problem. It looks like the TransferHttpCacheModule only works correctly for full urls. When I changed the call from this.http.get<any>('/api/blogposts') to this.http.get('http://localhost:4000/api/blogposts') everything worked as expected.

I'm not sure if it's a bug. It kind of make sense to have full urls when the server is rendering the page. But it should be documented I've spend few hours figuring that out.

Here is a simple HttpInterceptor that adds the url before API request only on server, that I now use

import { Injectable, Inject } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http';
import { Observable } from 'rxjs';
import { PLATFORM_ID } from '@angular/core';
import { isPlatformServer } from '@angular/common';

@Injectable()
export class ApiHttpInterceptor implements HttpInterceptor {
    constructor(@Inject(PLATFORM_ID) private platformId: Object) {  }

    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
         if (isPlatformServer(this.platformId)) {
            const url = `http://localhost:4000${req.url}`;
            req = req.clone({url: url});
         }
        return next.handle(req);
    }
}
sindorei1993 commented 5 years ago

@jandeu can you make an example repo or sth ? cause i got the same problem here .

kooldandy commented 5 years ago

@jandeu Thanks. I wasted my 5 hours to debug this issue.

AbdulRehman3 commented 5 years ago

Dynamic data is not showing up in Page Source Viewer All tags with angular code in template not showing in source viewer

AbdulRehman3 commented 5 years ago

I had some code which was throwing error on server side (most of which was about browser activities such as mouseover, click etc) I wrap up that code snippets in the following condition and everything worked as charm

if (isPlatformBrowser(this._platformId)) {
   // Code snippets specific to browser
}

Recommended: You should always check for server status as it indicates the error

keyuls commented 5 years ago

Dynamic data is not showing up in Page Source Viewer All tags with angular code in template not showing in source viewer

Have you found solution for this?

AbdulRehman3 commented 5 years ago

Have you found solution for this?

I had some code which was browser-specif that was causing error at server-side, so I wrapped that code-blocks in condition.

AbdulRehman3 commented 5 years ago

Is there any error in terminal? If yes, please provide it.

BruneXX commented 5 years ago

Hi Guys, any update on this?

AbdulRehman3 commented 5 years ago

Hi Guys, any update on this?

Hi, Can you check if there any error in the console (terminal)

AbdulRehman3 commented 5 years ago

@BruneXX I solved my problem this way.

I had some code which was throwing error on server side (most of which was about browser activities such as mouseover, click etc) I wrap up that code snippets in the following condition and everything worked as charm

if (isPlatformBrowser(this._platformId)) {
   // Code snippets specific to browser
}

Recommended: You should always check for server status as it indicates the error