Skip to content

Latest commit

 

History

History
274 lines (233 loc) · 8.77 KB

README.md

File metadata and controls

274 lines (233 loc) · 8.77 KB

Calcmaster Frontend

This project was generated with Angular CLI version 14.2.6.

Development server

Run ng serve for a dev server. Navigate to http://localhost:4200/de. The application will automatically reload if you change any of the source files.

Build

Run ng build --configuration production --localize --base-href / --deploy-url '' to build the project. The build artifacts will be stored in the target/classes/META-INF/resources/frontend directory.

Further help

To get more help on the Angular CLI use ng help or go check out the Angular CLI Overview and Command Reference page.

Adaptions to work with the Spring-Boot Application

angular.json

Beside the aspect, that this Application is an internationalized Application, there are some Changes within angular.json:

First of all the Output Path has been changed so that it will be written to target/classes/META-INF/resources/frontend:

...
"projects": {
  "calcmaster": {
                  ...
    "architect": {
      "build": {
        "builder": "@angular-devkit/build-angular:browser",
        "options": {
          "outputPath": "target/classes/META-INF/resources/frontend",
                     ...
        }
      }
    }
  }
}

The Directory target/classes is the Directory where Maven will compile all Java-Classes and copy all Resource Files to.
META-INF/resources is the Base-Directory for a Webjar.
frontend is the Identifier of the Webjar within the Spring Boot Application.

To work correctly with the internationalized Version of the Page, the baseHref, deployUrl and servePath Properties have to be set to the Default-Language (specified by i18n/sourceLocale).

...
"projects": {
  "calcmaster": {
                  ...
    "i18n": {
      "sourceLocale": "de"
    },
                  ...
    "architect": {
      "build": {
        "builder": "@angular-devkit/build-angular:browser",
        "options": {
          "outputPath": "target/classes/META-INF/resources/frontend",
          "baseHref": "/de/",
          "deployUrl": "/de/"
        }
      },
                   ...
      "serve": {
        "builder": "@angular-devkit/build-angular:dev-server",
        "options": {
          "servePath": "/de/"
        }
      }
    }
  }
}

Also add a Proxy-Configuration to the serve Section so that all Calls to /api and /webjars would be redirected to the Spring-Boot Application.

...
"projects": {
  "calcmaster": {
    "architect": {
                   ...
      "serve": {
        "builder": "@angular-devkit/build-angular:dev-server",
        "options": {
          "servePath": "/de/"
        },
        "configurations": {
          "development": {
            "browserTarget": "calcmaster:build:development",
            "proxyConfig": "src/proxy.conf.mjs"
          }
        },
        "defaultConfiguration": "development"
      }
    }
  }

package.json

While building the Angular Application it should ignore the baseHref and deployUrl Properties set especially for the Development. Therefor the build Task would disable this by Options:

"scripts": {
  "build": "ng build --configuration production --localize --base-href / --deploy-url \"\""
}

One should also specify, that ALL Locales should be generated by specifying the Option --localize.
Also note that the Option --deploy-url has been set to an empty String by using 2 Quotation Marks.

index.html

The File index.html will be manipulated to be a Thymeleaf Template:

<!doctype html>
<html lang="de" xmlns:th="http://www.thymeleaf.org">
<head>
  ...
  <base href="/" th:href="@{/}">
</head>
...

This is necessary for a used reverse Proxy Configuration. By using the Proxy-Header X-Forwarded-Prefix one can deploy the Application under any Context-Path and the relative URL's will work out of the Box.

API Path Interceptor (api-path.interceptor.ts)

A multilingual Angular Application will be built for every supported Language. Therefor it will be written to different Destination Directory (one for every supported Language).
But most of the Time the Application will call the same REST-APIs of the Backend WITHOUT the prefixed Language.

To work correctly an HTTP-Interceptor will be used to extract the Language out of the Path and put it as a Header-Information:

import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from "@angular/common/http";
import { Injectable } from "@angular/core";
import { Observable } from "rxjs";
import { Location } from "@angular/common";

@Injectable({ providedIn: 'root' })
export class ApiPathInterceptor implements HttpInterceptor {
  constructor(private _location: Location) {}

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    if (!req.url.startsWith("api/")) {
      return next.handle(req);
    }

    const url = this._location.prepareExternalUrl(`../${req.url}`);
    const language = Location.stripTrailingSlash(this._location.prepareExternalUrl('/')).split('/').pop();
    if (language) {
      return next.handle(req.clone({ url: url, setHeaders: { 'Accept-Language': language } }));
    }
    return next.handle(req.clone({ url: url }));
  }
}

Maven POM

The File pom.xml is the Definition of the Frontend Build. With the Frontend Maven Plugin one can compile the Angular Application using Apache Maven:

<plugin>
  <groupId>com.github.eirslett</groupId>
  <artifactId>frontend-maven-plugin</artifactId>
  <configuration>
    <installDirectory>target</installDirectory>
  </configuration>
  <executions>
    <execution>
      <id>install node and npm</id>
      <goals>
        <goal>install-node-and-npm</goal>
      </goals>
    </execution>
    <execution>
      <id>npm install</id>
      <goals>
        <goal>npm</goal>
      </goals>
      <configuration>
        <arguments>install -d</arguments>
      </configuration>
    </execution>
    <execution>
      <id>npm run build</id>
      <goals>
        <goal>npm</goal>
      </goals>
      <configuration>
        <arguments>run build</arguments>
      </configuration>
    </execution>
  </executions>
</plugin>

After creating the Angular Application the baseHref must be adapted so that the index.html would behave correctly as a multilingual Page by using the maven-replacer-plugin.

<plugin>
  <groupId>com.google.code.maven-replacer-plugin</groupId>
  <artifactId>maven-replacer-plugin</artifactId>
  <configuration>
    <regex>false</regex>
  </configuration>
  <executions>
    <execution>
      <id>replace /de</id>
      <phase>process-resources</phase>
      <goals>
        <goal>replace</goal>
      </goals>
      <configuration>
        <basedir>${project.build.outputDirectory}/META-INF/resources/frontend/de</basedir>
        <filesToInclude>index.html</filesToInclude>
        <replacements>
          <replacement>
            <token><![CDATA[<base href="/de/" th:href="@{/}">]]></token>
            <value><![CDATA[<base href="/de/" th:href="@{/de/}">]]></value>
          </replacement>
        </replacements>
      </configuration>
    </execution>
    <execution>
      <id>replace /en</id>
      <phase>process-resources</phase>
      <goals>
        <goal>replace</goal>
      </goals>
      <configuration>
        <basedir>${project.build.outputDirectory}/META-INF/resources/frontend/en</basedir>
        <filesToInclude>index.html</filesToInclude>
        <replacements>
          <replacement>
            <token><![CDATA[<base href="/en/" th:href="@{/}">]]></token>
            <value><![CDATA[<base href="/en/" th:href="@{/en/}">]]></value>
          </replacement>
        </replacements>
      </configuration>
    </execution>
  </executions>
</plugin>

The Replacement of the baseHref must be executed for every supported Language (in this case for de and en).
Be aware that the trailing Slash (/de/ or /en/) is necessary so that the Browser can append the Base HREF to any relative URL of the Angular Application.

API-URLs

Be aware that you have to use relative URLs when you communicate with the Backend via httpClient. Using absolute URLs will not use the baseHref and won't work when deployed with a Context-Path others than /.

It is also a good common Practice to use APP_BASE_HREF and the LocationService of Angular.
With the LocationService one can prepare external URLs which uses the correct baseHref.