Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular Universal does not work with Angular Google Maps

Tech: Angular Cli, Angular version 7, Angular Google Maps, Firebase Functions.

Issue: I'm trying to serve my angular universal app but getting an error for angular google maps when building.

Error I get:

/Users/test/Public/Leisure/leisure-app/functions/node_modules/@agm/core/services/managers/info-window-manager.js:1
(function (exports, require, module, __filename, __dirname) { import {
Observable } from 'rxjs';
                                                              ^^^^^^

SyntaxError: Unexpected token import
    at createScript (vm.js:80:10)
    at Object.runInThisContext (vm.js:139:10)

Seems that Angular Universal doesnt like third party libraries.

The resource I followed: https://hackernoon.com/deploying-angular-universal-v6-with-firebase-c86381ddd445

My App Module:

import { AgmCoreModule } from '@agm/core';
import { AngularFireAuthModule } from '@angular/fire/auth';
import { AngularFireDatabase } from '@angular/fire/database';
import { AngularFireModule } from '@angular/fire';
import { ServiceWorkerModule } from '@angular/service-worker';
import { AngularFireStorageModule } from '@angular/fire/storage';
import { AngularFirestoreModule } from '@angular/fire/firestore';
import { BrowserModule } from '@angular/platform-browser';
import { CommonModule } from '@angular/common';
import { GooglePlaceModule } from 'ngx-google-places-autocomplete';
import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http';
import { NgModule, NO_ERRORS_SCHEMA, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { NgxErrorsModule } from '@hackages/ngxerrors';
import { ReactiveFormsModule, FormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';
import { TruncateModule } from 'ng2-truncate';
import { MetaModule } from '@ngx-meta/core';
import { PreventDoubleSubmitModule } from 'ngx-prevent-double-submission';

// Core App
import { AppComponent } from './app.component';
import { CoreInterceptor } from './interceptor';
import { environment } from '../environments/environment';

// Modules
import { SharedModule } from './shared/shared.module';
import { CoreModule } from './services/core.module';
import { LayoutModule } from './layouts/layout.module';

// Sections
import { COMPONENTS } from './components/index';
import { ROUTES } from './app.routes';

// Guards
import { AuthGuard } from './guards/auth.guard';
import { CreditGuard } from './guards/credit.guard';

@NgModule({
  declarations: [
    AppComponent,
    COMPONENTS
  ],
  imports: [
    CommonModule,
    FormsModule,
    AngularFireModule.initializeApp(environment.firebase),
    AngularFireAuthModule,
    AngularFirestoreModule,
    AngularFireStorageModule,

    // This is the Angular google maps module causing issue on build
    AgmCoreModule.forRoot({
      apiKey: environment.googleApiKey,
      libraries: ['places']
    }),

    GooglePlaceModule,
    LayoutModule,
    BrowserModule.withServerTransition({ appId: 'test123' }),
    PreventDoubleSubmitModule.forRoot(),
    TruncateModule,
    MetaModule.forRoot(),
    HttpClientModule,
    NgxErrorsModule,
    ReactiveFormsModule,
    RouterModule.forRoot(ROUTES),
    CoreModule,
    SharedModule,
    ServiceWorkerModule.register('/ngsw-worker.js', { enabled: environment.production })
  ],
  providers: [
    { provide: HTTP_INTERCEPTORS, useClass: CoreInterceptor, multi: true },
    AuthGuard,
    CreditGuard,
    AngularFireDatabase
  ],
  exports: [ RouterModule ],
  schemas: [ NO_ERRORS_SCHEMA, CUSTOM_ELEMENTS_SCHEMA ],
  bootstrap: [ AppComponent ]
})
export class AppModule { }

Command I run: serve: npm run build && firebase serve --only functions

Latest Error (for npm run serve:ssr):

enter image description here

Error code from serve:ssr:

e.Lb=function(a,b,c){a=this.ka.J[String(a)];if(!a)return!0;a=a.concat();for(var d=!0,f=0;f<a.length;++f){var g=a[f];if(g&&!g.Sa&&g.capture==b){var k=g.listener,p=g.Ob||g.src;g.Eb&&this.Le(g);d=!1!==k.call(p,c)&&d;}}return d&&0!=c.Be};e.jb=function(a,b,c,d){return this.ka.jb(String(a),b,c,d)};var ub=h.JSON.stringify;function vb(a,b){this.Sf=100;this.ef=a;this.ug=b;this.Zb=0;this.Pb=null;}vb.prototype.get=function(){if(0<this.Zb){this.Zb--;var a=this.Pb;this.Pb=a.next;a.next=null;}else a=this.ef();return a};vb.prototype.put=function(a){this.ug(a);this.Zb<this.Sf&&(this.Zb++, a.next=this.Pb, this.Pb=a);};function I(){this.lc=this.Va=null;}var xb=new vb(function(){return new wb},function(a){a.reset();});I.prototype.add=function(a,b){var c=this.Af();c.set(a,b);this.lc?this.lc.next=c:this.Va=c;this.lc=c;};I.prototype.remove=function(){var a=null;this.Va&&(a=this.Va, this.Va=this.Va.next, this.Va||(this.lc=null), a.next=null);return a};I.prototype.wg=function(a){xb.put(a);};I.prototype.Af=function(){return xb.get()};function wb(){this.next=this.scope=this.Gc=null;}
wb.prototype.set=function(a,b){this.Gc=a;this.scope=b;this.next=null;};wb.prototype.reset=function(){this.next=this.scope=this.Gc=null;};function yb(a){h.setTimeout(function(){throw a;},0);}var zb;
like image 695
AngularM Avatar asked Apr 04 '19 13:04

AngularM


People also ask

Does Google Maps use angular?

The new Angular Component pearl-lullaby (v9. 0.0-rc. 0) introduces the second official @angular/component component, a Google Maps component.

Is angular universal necessary?

Key Benefits for SEO A primary benefit for using Angular Universal is that it improves web crawler support for enhanced Search Engine Optimization (SEO). With traditional client-side rendered SPAs, anything that is not in that shell of an . html is all rendered by the JavaScript.

Why angular Universal is being used?

Angular Universal executes on the server, generating static application pages that later get bootstrapped on the client. This means that the application generally renders more quickly, giving users a chance to view the application layout before it becomes fully interactive.


1 Answers

TL;DR:

Source code and DEMO

enter image description here


The issue here is that @agm/core package is compiled with es2015 module. As a result, it contains import and export in js code.

To remedy this you have two main options:

1. Compile @agm/core package to commonjs format.

You can use either babel or typescript to compile that package. Then you need to make sure you provided compiled version in your functions dependencies

functions/package.json

"dependencies": {
  ...
  "@agm/core": "file:./@agm/core"
},

Here I use local dependency but you can also use your own published version.

Another method would be compile directly in node_modules and publish the whole node_modules(I would avoid this):

firebase.json

{
  "functions": {
    "ignore": []
  }
}

How to compile?

Babel

Install dependencies in root directory.

npm i -D @babel/cli @babel/core @babel/preset-env

Use the following script to compile:

package.json

"postbuild": "babel node_modules/@agm/core -d functions/@agm/core --presets @babel/preset-env && node ./copy-package-json.js"

where

copy-package-json.js

const fs = require('fs-extra');
const { join } = require('path');

(async() => {
  await fs.copy(join(process.cwd(), 'node_modules/@agm/core/package.json'),
                join(process.cwd(), 'functions/@agm/core/package.json'));
})();

Typescript

tsconfig.agm.json

{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "outDir": "./functions/@agm/core",
    "types": [],
    "module": "commonjs"
  },
  "include": [
    "node_modules/@agm/core"
  ]
}

package.json

"postbuild": "tsc -p tsconfig.agm.json --allowJs && node ./copy-package-json.js"

2. Generate server bundle

This is what Angular universal tutorial uses so I prefer this solution.

Also follow this quide

Simple steps:


1. Install global dependencies

I have installed:

2. Create a new Angular project

ng new angular-agm

3. Add Angular universal

ng add @nguniversal/express-engine --clientProject angular-agm

4. Update server.ts

Export the express app, then remove the call to listen and change index to index2.

import 'zone.js/dist/zone-node';
import {enableProdMode} from '@angular/core';
// Express Engine
import {ngExpressEngine} from '@nguniversal/express-engine';
// Import module map for lazy loading
import {provideModuleMap} from '@nguniversal/module-map-ngfactory-loader';

import * as express from 'express';
import {join} from 'path';
import * as path from 'path';

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

// Express server
export const app = express();

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

const index = require('fs')
  .readFileSync(path.resolve(DIST_FOLDER, 'index2.html'), 'utf8')
  .toString();

const domino = require('domino');
const win = domino.createWindow(index);
global['window'] = win;
global['document'] = win.document;

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

// 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', DIST_FOLDER);

// Example Express Rest API endpoints
// app.get('/api/**', (req, res) => { });
// Serve static files from /browser
app.get('*.*', express.static(DIST_FOLDER, {
  maxAge: '1y'
}));

// All regular routes use the Universal engine
app.get('*', (req, res) => {
  res.render('index2', { req });
});

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

5. Build

npm run build:ssr

where build:ssr

"build:ssr": "npm run build:client-and-server-bundles && npm run compile:server && node ./tools/copy-artifacts.js",

copy-artifacts.js

const fs = require('fs-extra');
const { join } = require('path');

(async() => {
  const src = join(process.cwd(), 'dist');
  const copy = join(process.cwd(), 'functions/dist');

  await fs.rename(join(src, 'browser/index.html'), join(src, 'browser/index2.html'));
  await fs.remove(copy);
  await fs.copy(src, copy);
})();

6. Update functions/index.js to use built version of express app

const functions = require('firebase-functions');

const { app } = require('./dist/server');

exports.ssr = functions.https.onRequest(app);

7. Configure firebase.json

{
  "hosting": {
    "public": "dist/browser",
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ],
    "rewrites": [
      {
        "source": "**",
        "function": "ssr"
      }
    ]
  }
}

The source code can be found on Github

See also demo https://angular-agm.firebaseapp.com/

like image 59
yurzui Avatar answered Sep 23 '22 19:09

yurzui