Connecting an Angular Single Page Application (SPA) with platformOS
Problem
How to connect your Angular Single Page Application (SPA) that you created with the CLI to platformOS? The goal is to have your Angular app ready for local development communicating with the Instance by the API via proxy.
This article is based on Angular version 8.
To follow this article, you should have your development environment and a platformOS Instance set up, be familiar with pages and layouts, and have the Angular CLI installed.
Solution
Connecting Angular with platformOS is a six-step process. Angular CLI is based on webpack so some steps will be valid for other frameworks/builds based on webpack.
Step 1: Create place for Angular App files
The source files of a web app are not part of platformOS and can be stored anywhere you want.
In this example, to keep it together in one repo and in one project, you'll create the Angular app directly in your Instance's folder but not inside the /app
folder. This is recommended but not required.
Navigate to your Instance folder and create new app with:
$ng new web-app
Pick scss as stylesheet format
.
Instead of web-app
you can also use another name for your project.
Step 2: Set up local development, production build and "hosting"
Angular app build files are part of the platformOS Instance.
In platformOS, there's no traditional hosting
. You cannot put files straight into some root
folder and have your-domain.com/the-file.html
.
But there's a special place in your Instance where you can achieve a similar effect with a static address.
Each file put into app/assets/
will be "visible" as {your-uploads-path}/assets/
.
For example app/assets/images/bg.jpg
can be accessed also as {your-uploads-path}/assets/images/bg.jpg
.
Read about assets for more information.
How to get your-uploads-path
?
Put this code into some page liquid file and check what the server returns in the website's source code:
{{ "" | asset_url }}
It will look similar to:
https://uploads.staging.oregon.platform-os.com/instances/<YOUR-INSTANCE-ID>/assets/
We strongly recommend to wrap your build files into another folder like app/assets/web-app
, because in the future you might want to store other assets, for example graphic files for emails, in the app/assets/
folder.
Change the location to store build files in the web-app/angular.json
config file.
Find the line with "outputPath": "dist/web-app",
and change it into:
"outputPath": "../app/assets/web-app",
If you are using version control like GIT, it's recommended to add this folder into your .gitignore
rules.
After building your app, files will now be copied into app/assets/web-app
.
But now compiled files will be stored under app/assets/web-app
(like for example main.js
) and Angular will be looking for it in the root folder. You need to let it know where to find it.
Open web-app/package.json
and in scripts
extend the build
command:
"build": "ng build --prod --base-href / --deploy-url your-uploads-path/assets/web-app/",
Now your Angular app routing will know that your base-href is /
and compiled files will be stored in /assets/web-app/
.
It will append proper paths into index.html automatically.
Step 3: Set up routing and redirects
platformOS routing is based on Liquid files (pages and layouts).
Assuming that the Angular router will do its job, you'll need to rewrite all URLs into one file.
You can do this with pages and layouts. Learn More about Pages and Layouts.
First, create a layout that will be injected into every Liquid page. This file will be generated during build by Angular and copied into app/views/layouts
as app.html.liquid
.
You can do this in three steps:
- open
web-app/src
and change the nameindex.html
toapp.html.liquid
(you can name it as you want, but it has to have a.liquid
extension) - open the
web-app/angular.json
config file, find the line with"index": "src/index.html",
and change it to"index": "src/app.html.liquid",
- open
web-app/package.json
and add commands
"copyIndex": "cp ../app/assets/web-app/app.html.liquid ../app/views/layouts/",
and
"postbuild": "npm run copyIndex"
So right after your web app will be built, your index
will be copied into app/views/layouts
as a layout with the name app
.
Now create the home page.
You can do this in app/views/pages
where you create the routing
folder.
Now, in app/views/pages/routing
, create the index.liquid
page with the following code:
app/views/pages/routing/index.liquid
---
layout: app
---
You will need a similar page for every URL, but fortunately only on the first level.
For example, have the URLs:
- company/about
- company/terms-of-service
- company/info/some-example-page
For all these links, only one page has to be created:
app/views/pages/company.html.liquid
---
layout: app
---
The Angular router will do rest of the job.
Step 4: Set up local and production paths
If you needed to add some asset files like for example images or custom svg icons for Angular Materials or maybe translation files like for ngx-translate, you should definitely store them in web-app/src/assets/
.
Now you need to let your app know where you store them. You will use Angular environments
files for this task.
web-app/src/environments/environment.ts
export const environment = {
production: false,
assetsPrefix: ''
}
web-app/src/environments/environment.prod.ts
export const environment = {
production: true,
assetsPrefix: 'your-uploads-path/assets/web-app',
}
Anywhere in code where you need a reference to one of your assets, you'll use the environment.assetsPrefix
variable.
Step 5: Set up local and production paths inside (s)css files
Angular CLI cannot replace css files because css is built before the replacement engine runs.
There are some different approaches to achieve that, but in this tutorial, we go another way.
Create two files:
web-app/src/styles/environments/local/asset-path.scss
$assets-path: '';
web-app/src/styles/environments/prod/asset-path.scss
$assets-path: 'your-uploads-path/assets/web-app/';
(web-app/src/styles/
is where you store all global scss files)
Then create two commands that will do the job - before angular build will change the $assets-path: ''
into $assets-path: '/assets/web-app/'
and restore it after build.
"cssAssetsPath": "cp src/styles/environments/prod/asset-path.scss src/styles/environments/",
"cssAssetsPathReset": "cp src/styles/environments/local/asset-path.scss src/styles/environments/",
Also, add the "prebuild": "npm run cssAssetsPath",
command to run it before and update postbuild
so that's how your scripts look like now:
"cssAssetsPath": "cp src/styles/environments/prod/asset-path.scss src/styles/environments/",
"cssAssetsPathReset": "cp src/styles/environments/local/asset-path.scss src/styles/environments/",
"copyIndex": "cp ../app/assets/web-app/app.html.liquid ../app/views/layouts/",
"prebuild": "npm run cssAssetsPath",
"build": "ng build --prod --base-href / --deploy-url your-uploads-path/assets/web-app/",
"postbuild": "npm run copyIndex && npm run cssAssetsPathReset"
And now in any of your scss
files when you need assets you'll use:
@import '~styles/environments/asset-path.scss';
.my-component {
background-image: url('#{$assets-path}/assets/images/some-image.jpg');
}
Step 6: Set up proxy for local API calls
Due to security issues you should not use:
response_headers: >
{
"Access-Control-Allow-Origin": "*"
}
So you need to use a proxy. To do this, follow the instructions here: Proxying to a backend server.
Now you have $ng serve
for local development and $npm run build
for production build.
Source code
You can find the source code of this example on GitHub.