Ошибки при запуске Express Server с Node.js с Universal Angular 5 - PullRequest
0 голосов
/ 11 мая 2018

У меня проблемы с твио.

Во-первых, я не могу передать queryParam из server.ts в AppCompoment.ts.

Во-вторых, служба translateService выдает ОШИБКУ [Ошибка] без сообщения, когда выполняется на сервере, но работает нормально, когда выполняется в браузере.

Вот код частей проекта:

server.ts

// These are important and needed before anything else
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';

(global as any).WebSocket = require('ws');
(global as any).XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest;


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

// Express server
const app = express();

//app.urlencoded({extended: false});

const PORT = process.env.PORT || 4000;
const HTTPS_PORT = process.env.HTTPS_PORT || 4443;

const KEY_CERTIFICATE = process.env.KEY_CERTIFICATE;
const CRT_CERTIFICATE = process.env.CRT_CERTIFICATE;
const PASSWORD_CERTIFICATE = process.env.PASSWORD_CERTIFICATE;

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';

app.engine('html', ngExpressEngine({
  bootstrap: AppServerModuleNgFactory,
  providers: [
    provideModuleMap(LAZY_MODULE_MAP)
  ]
}));

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

// Our page routes
export const routes: string[] = [
  'main',
  'dashboard',
  'dashboard/contact',
  'dashboard/blog'
];

// All regular routes use the Universal engine
app.get('/', (req, res) => {
  console.time(`GET: ${req.originalUrl}`);
  res.render(join(DIST_FOLDER, 'browser', 'index.html'), { req, res } );
  console.timeEnd(`GET: ${req.originalUrl}`);
});

routes.forEach(route => {
  app.get(`/${route}`, (req, res) => {
    //res.json({'lang': req.query.lang});
    console.log(req.query.lang);
    console.time(`GET: ${req.originalUrl}`);
    res.render(join(DIST_FOLDER, 'browser', 'index.html'), { req, res } );
    console.timeEnd(`GET: ${req.originalUrl}`);
  });
  app.get(`/${route}/*`, (req, res) => {
    //res.json({'lang': req.query.lang});
    console.log(req.query.lang);
    console.time(`GET: ${req.originalUrl}`);
    res.render(join(DIST_FOLDER, 'browser', 'index.html'), { req, res } );
    console.timeEnd(`GET: ${req.originalUrl}`);
  });
});

// Server static files from /browser
app.get('/web', express.static(join(DIST_FOLDER, 'browser'), { 'index': false }));

app.get('/**', express.static(join(DIST_FOLDER, 'browser')));

// All other routes must be resolved if exist
/*
app.get('*', function(req, res) {
  res.render(join(req.url), { req });
});
*/

var http = require('http');

var httpServer = http.createServer(app);

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

if(KEY_CERTIFICATE && CRT_CERTIFICATE && PASSWORD_CERTIFICATE) {

  var fs = require('fs');
  var https = require('https');

  var privateKey  = fs.readFileSync(KEY_CERTIFICATE, 'utf8');
  var certificate = fs.readFileSync(CRT_CERTIFICATE, 'utf8');

  var credentials = {
    key: privateKey,
    cert: certificate,
    passphrase: PASSWORD_CERTIFICATE
  };
  var httpsServer = https.createServer(credentials, app);

  // Start up the Node server at HTTP_PORT
  httpsServer.listen(HTTPS_PORT, () => {
    console.log(`Node server listening on http://localhost:${HTTPS_PORT}`);
  });
}

AppServerModule.ts

import { NgModule } from '@angular/core';
import { ServerModule, ServerTransferStateModule } from '@angular/platform-server';
import { ModuleMapLoaderModule } from '@nguniversal/module-map-ngfactory-loader';

import { AppModule } from './app.module';
import { TemplateComponent } from '../template/template.component';

@NgModule({
  imports: [
    // The AppServerModule should import your AppModule followed
    // by the ServerModule from @angular/platform-server.
    AppModule,
    ServerModule,
    ModuleMapLoaderModule, // <-- *Important* to have lazy-loaded routes work
    ServerTransferStateModule,
  ],
  // Since the bootstrapped component is not inherited from your
  // imported AppModule, it needs to be repeated here.
  bootstrap: [TemplateComponent],
})
export class AppServerModule {
  constructor() {
    console.log('AppServerModule');
  }
}

AppModule:

import { NgModule } from '@angular/core';
import { BrowserModule, BrowserTransferStateModule } from '@angular/platform-browser';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { enableProdMode } from '@angular/core';
import { HttpClientModule, HttpClient } from '@angular/common/http';
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
import { TranslateModule, TranslateLoader } from '@ngx-translate/core';
// Routing
import { Router } from '@angular/router';
import { RouterModule, Routes } from '@angular/router';
// Firebase
import { AngularFireModule } from 'angularfire2';
import { environment } from '../../environments/environment';
import { AngularFireDatabase } from 'angularfire2/database';
import { AngularFireAuthModule } from 'angularfire2/auth';
import { AuthService } from '../firebase-auth/auth.service';
import { FirebaseAuthComponent } from '../firebase-auth/firebase-auth.component';
// My Components
import { TemplateComponent } from '../template/template.component';
import { MainComponent } from '../main/main.component';
import { BlogComponent } from '../blog/blog.component';
import { ProjectsComponent } from '../projects/projects.component';
import { ProjectComponent } from '../project/project.component';
import { FormComponent } from '../form/form.component';
import { DashboardComponent } from '../dashboard/dashboard.component';
import { DashboardBlogComponent } from '../dashboard/dashboard-blog.component';
import { DashboardContactComponent } from '../dashboard/dashboard-contact.component';
import { ExperienceComponent } from '../experience/experience.component';
import { ProjectService } from '../projects/project.service';
import { ExperienceService } from '../experience/experience.service';
import { PipesModule } from '../pipes/pipes.module';
import { LanguageService } from '../template/language.service';

const appRoutes: Routes = [
  {
    path: 'main',
    component: MainComponent
  },
  {
    path: 'blog',
    component: BlogComponent
  },
  {
    path: 'dashboard',
    component: DashboardComponent,
    children: [
      {
        path: '',
        redirectTo: 'blog',
        pathMatch: 'full'
      },
      {
        path: 'blog',
        component: DashboardBlogComponent
      },
      {
        path: 'contact',
        component: DashboardContactComponent
      }
    ]
  },
  {
    path: '',
    redirectTo: '/main',
    pathMatch: 'prefix'
  },
  {
    path: '**',
    redirectTo: '/main',
    pathMatch: 'prefix'
  }
];

@NgModule({
  declarations: [
    FirebaseAuthComponent,
    MainComponent,
    BlogComponent,
    TemplateComponent,
    ProjectsComponent,
    ProjectComponent,
    FormComponent,
    DashboardComponent,
    DashboardBlogComponent,
    DashboardContactComponent,
    ExperienceComponent,
  ],
  imports: [
    BrowserModule.withServerTransition({appId: 'davidmartinezros.com'}),
    BrowserTransferStateModule,
    FormsModule,
    ReactiveFormsModule,
    HttpModule,
    HttpClientModule,
    RouterModule.forRoot(appRoutes),
    AngularFireModule.initializeApp(environment.firebase),
    AngularFireAuthModule,
    PipesModule,
    TranslateModule.forRoot({
      loader: {
        provide: TranslateLoader,
        useFactory: (createTranslateLoader),
        deps: [HttpClient]
      }
    })
  ],
  providers: [AuthService, AngularFireDatabase, BlogComponent, DashboardContactComponent, ProjectService, ExperienceService, LanguageService],
  bootstrap: [TemplateComponent]  // main (first) component
})

export class AppModule {
  constructor() {
    console.log('AppModule');
  }
}
/*
if (!/localhost/.test(document.location.host)) {
  enableProdMode();
}
*/
export function createTranslateLoader(http: HttpClient) {
  return new TranslateHttpLoader(http, './assets/i18n/', '.json');
}

TemplateComponent.ts:

import { Component, ViewChild, ElementRef, Inject } from '@angular/core';
import { ActivatedRoute, RouterModule } from '@angular/router';
import { AuthService } from '../firebase-auth/auth.service';
import { TranslateService } from '@ngx-translate/core';
import { ProjectsComponent } from '../projects/projects.component';
import { ExperienceComponent } from '../experience/experience.component';
import { LanguageService } from './language.service';
import { Pipe } from '@angular/core';
import { Language } from './language';
import { Title, Meta } from '@angular/platform-browser';
import { PLATFORM_ID } from '@angular/core';
import { isPlatformBrowser, isPlatformServer } from '@angular/common';

@Component({
    selector: 'app-template',
    templateUrl: '../template/template.component.html'
})

export class TemplateComponent {

    objectKeys = Object.keys;

    languages: Language[];

    language;

    loaded: boolean = false;

    playing: boolean = false;

    sound: any;

    constructor(@Inject(PLATFORM_ID) private platformId: Object,
        public authService: AuthService,
        private translate: TranslateService,
        private route: ActivatedRoute,
        private languageService: LanguageService,
        private titleService: Title,
        private metaService: Meta) {

        this.getLanguanges();
    }

    ngOnInit() {
        if (isPlatformBrowser(this.platformId)) {
            // Client only code.
            this.loadLanguage();
            this.loadMusic();
         }
         if (isPlatformServer(this.platformId)) {
           // Server only code.
           this.loadServerLanguage();
         }
    }

    loadMusic() {
        this.sound = new Audio();
        this.sound.autoplay = false;
        this.sound.preload = 'auto';
        this.sound.autobuffer = true;

        let parent = this;

        this.sound.addEventListener('loadeddata', function() {
            parent.loaded = true;
        }, false);

        this.sound.addEventListener('play', function() {
            parent.playing = true;
        }, false);

        this.sound.addEventListener('pause', function() {
            parent.playing = false;
        }, false);

        this.sound.src = './assets/audio/Rhodesia_MkII.mp3';
        this.sound.load();
    }

    loadLanguage() {
        var userLang = "";

        //console.log(this.route);

        console.log(this.route.queryParams);

        this.route.queryParams.subscribe(params => {
            if(!params['lang'] || params['lang'] == "") {
                userLang = this.language;
            } else {
                userLang = params['lang'];
            }

            console.log("queryParams:" + userLang);

            if(!userLang || userLang == "") {
                userLang = navigator.language;
                if(userLang.startsWith("zh")) {
                    userLang = "zh";
                }
            }

            if(userLang) {
                userLang = userLang.toLowerCase();
            }

            if(userLang && userLang.length > 2) {
                userLang = userLang.substring(0,2);
            }

            if(userLang == "es" || userLang == "en" || userLang == "zh") {
                this.changeLanguage(userLang);
            } else {
                this.changeLanguage("en");
            }

            console.log('complete loadLanguage');

        });
    }

    loadServerLanguage() {
        var userLang = "";

        //console.log(this.route);

        console.log(this.route.queryParams);

        this.route.queryParams.subscribe(params => {
            if(!params['lang'] || params['lang'] == "") {
                userLang = this.language;
            } else {
                userLang = params['lang'];
            }

            console.log("queryParams:" + userLang);

            if(userLang) {
                userLang = userLang.toLowerCase();
            }

            if(userLang && userLang.length > 2) {
                userLang = userLang.substring(0,2);
            }

            if(userLang == "es" || userLang == "en" || userLang == "zh") {
                this.changeLanguage(userLang);
            } else {
                this.changeLanguage("en");
            }

            console.log('complete loadLanguage');

        });
    }

    isLoadedTrack() {
        return this.loaded;
    }

    isPlayingTrack() {
        return this.playing;
    }

    playTrack() {
        if(this.sound) {
            this.sound.play();
        }
    }

    pauseTrack() {
        if(this.sound) {
            this.sound.pause();
        }
    }

    public changeServerLanguage(language) {

        console.log(language);

        console.log("Ara anem a cridar a this.translate.setDefaultLang(language); que al servidor dona error.");

        // this language will be used as a fallback when a translation isn't found in the current language
        this.translate.setDefaultLang(language);

        // the lang to use, if the lang isn't available, it will use the current loader to get them
        this.translate.use(language);

        this.language = language;

        // Sets the <title></title>
        this.translate.get("TitleIndex")
            .toPromise()        
            .then(title => this.titleService.setTitle(title))
            .catch(this.handleError);

        // Sets the <meta> tag author
        this.translate.get("TagAuthorIndex")
            .toPromise()        
            .then(author => this.metaService.updateTag({ name: 'author', content: author }))
            .catch(this.handleError);

        // Sets the <meta> tag keywords
        this.translate.get("TagKeywordsIndex")
            .toPromise()        
            .then(keywords => this.metaService.updateTag({ name: 'keywords', content: keywords }))
            .catch(this.handleError);

        // Sets the <meta> tag description
        this.translate.get("TagDescriptionIndex")
            .toPromise()        
            .then(description => this.metaService.updateTag({ name: 'description', content: description }))
            .catch(this.handleError);

        console.log('changeServerLanguage');

    }

    public changeLanguage(language) {

        console.log(language);

        // this language will be used as a fallback when a translation isn't found in the current language
        this.translate.setDefaultLang(language);

        // the lang to use, if the lang isn't available, it will use the current loader to get them
        this.translate.use(language);

        this.language = language;

        ProjectsComponent.updateStuff.next(false);
        ExperienceComponent.updateStuff.next(false);

        this.getLanguanges();

        // Sets the <title></title>
        this.translate.get("TitleIndex")
            .toPromise()        
            .then(title => this.titleService.setTitle(title))
            .catch(this.handleError);

        // Sets the <meta> tag author
        this.translate.get("TagAuthorIndex")
            .toPromise()        
            .then(author => this.metaService.updateTag({ name: 'author', content: author }))
            .catch(this.handleError);

        // Sets the <meta> tag keywords
        this.translate.get("TagKeywordsIndex")
            .toPromise()        
            .then(keywords => this.metaService.updateTag({ name: 'keywords', content: keywords }))
            .catch(this.handleError);

        // Sets the <meta> tag description
        this.translate.get("TagDescriptionIndex")
            .toPromise()        
            .then(description => this.metaService.updateTag({ name: 'description', content: description }))
            .catch(this.handleError);

    }

    getLanguanges(): void {
        this.languageService.getLanguages()
            .then(languages => 
            { this.languages = languages }
        );
    }

    private handleError(error: any): Promise<any> {
        console.error('An error occurred', error); // for demo purposes only
        return Promise.reject(error.message || error);
    }
}
...