0

I have a simple web application in Angular v5. This is a purely client-side application in the meaning of it doesn't use any server-side (this is a basic calculator application that load data from JSON file with some measurements and display to the user some analysis according to that JSON).

During development I've tested the application using the Angular-cli "serve", by running - ng serve command, and it works great.

Now I wont to bundle my app and start using it. I would like to run this app locally on my machine without any server.
According to the Angular documentation, to bundle the application to deployment, I should run the command: ng build --prod --base-href=./. There to flag --prod will deploy a minified and uglified version of the application. And the --base-href=./ will set <base href="./"> in the index.html to allow me loading the application files (all the ABC.bundles.js file) directly from the local machine.

Now, when I'm opening the dist/index.html file I can see that all the scripts have loaded, however, I have an error in the console:

ERROR Error: Uncaught (in promise): SecurityError: Failed to execute 'replaceState' on 'History': A history state object with URL 'file:///C:/.../dist/' cannot be created in a document with origin 'null' and URL 'file:///C:/.../dist/index.html'.

How do I deploy/bundle my application to run locally, without a server?

Gil Epshtain
  • 8,670
  • 7
  • 63
  • 89
  • @David, This is a completely different issue. Both issues have the same error in the console, but the scenario and the problem are completely different – Gil Epshtain Mar 19 '18 at 14:20
  • It's not different, it's a limitation from the browser that you can't use history API on local files – David Mar 19 '18 at 14:25
  • Yes. Even if you're not using the History API directly, Angular is, behind the curtain. – Oscar Paz Mar 19 '18 at 14:25
  • But how is it related to Angular deployment? – Gil Epshtain Mar 19 '18 at 14:25
  • That's the point, it is not. You need a webserver if you want to use history API, angular or not. Or try the workaround suggested in the link I posted – David Mar 19 '18 at 14:30
  • I didn't explicitly invoke `history-api`, what Angular component/service uses this API? maybe I can emit that component/service? – Gil Epshtain Mar 19 '18 at 14:32

5 Answers5

2

When using angular-router you need a server-side since the router is used to serve pages.

To solve these issue you have 2 choices:

  1. Use some sort of light-weight server like NPM's http-server which allow you to run a server on a local machine from the command-line.

  2. Drop the angular-router completely - Yes this is a bit extreme solution and I'm sure it will not be a valid solution to anybody with more than 2 pages in his application. But if you have a very very simple app with one page only and you don't need routing capabilities then this is a valid solution.

Gil Epshtain
  • 8,670
  • 7
  • 63
  • 89
1

Try using HashLocationStrategy

https://angular.io/api/common/HashLocationStrategy https://angular.io/guide/router#hashlocationstrategy

In your module

imports [
RouterModule.forRoot(routes, {useHash: true})
]
David
  • 33,444
  • 11
  • 80
  • 118
1

In your routes configuration add;

RouterModule.forRoot(routes, {useHash: true});

In your index.html do this;

<!-- <base href="/"> --> <script>document.write('<base href="' + document.location + '" />');</script>

And then build with --base-href ./ e.g

ng build --prod --base-href ./

source https://github.com/angular/angular/issues/13948

theeGwandaru
  • 400
  • 2
  • 14
1

i have also faced same issue and it was not working after deployment into server, i have added HashLocationStrategy.

https://angular.io/api/common/HashLocationStrategy

https://angular.io/guide/router#hashlocationstrategy

for me page was not loading and throwing 404 page not found after requesting from Google.com

url --- http://techeducation.in

i have solved this after commenting or removing ...

<base href="http://www.techeducation.in/" /> from index.html file of dist folder.

Sanjay Tiwari
  • 133
  • 1
  • 2
  • 9
0

Since Angular 13 despricated deployUrl. Cause trouble if you using CDN cache but your app running on different domain like heroku or something else.

here is the solution step by step:

  1. delete deployUrl, no need to change it to baseHref, we keep it default

  2. replace url to load it manually by replace-file.js:

const replace = require('replace-in-file');

const prefix = 'https://your-cdn.com/assets';

const action2options = {
  files: path.join(process.cwd(), '/dist/browser/index.html'),
  from: [/<script src="(.*?)"/g],
  to: [`<script src="${prefix}$1"`],
};


replace(action2options)
  .then(results => {
    // console.log('Replacement results:', results);
  })
  .catch(error => {
    console.error('Error occurred:', error);
  });
  1. load the chunk using your assets CDN by add this line to main.ts (if use universal, add it to main.server.ts too:
// main.ts add it
// main.server.ts add it too if use universal
// https://github.com/angular/angular-cli/issues/22113#issuecomment-1004279867
declare var __webpack_public_path__: string;
__webpack_public_path__ = 'https://your-cdn.com/assets';

if you need change link by environment, use this to detect:

// angular.json => add to build -> scripts file

            {
                "input": "src/environments/replacer-build.js",
                "inject": false,
                "bundleName": "replacer-build.development"
              }
// update to production build too:
  "configurations": {
            "production": {
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/environment.prod.ts"
                }
              ],

after that you can detect it in replace-file.js by this:

const fs = require('fs');

const files = fs.readdirSync(path.join(process.cwd(), '/dist/browser')).filter(file => {
  return file.match(/^replacer-build\.(development|production|staging)\.js$/);
});

let env = '';
let assetUrl = '';
if (files.length > 0) {
  const filename = files[0];
  if (filename.includes('development')) {
    env = 'development';
    assetUrl = 'http://localhost:4200/';
  else if (filename.includes('production')) {
    env = 'production';
    assetUrl = 'https://your-cdn.com/assets';
  }
}


console.log(`The current environment is ${env} and assetURL replace to:`, assetUrl);

Hiep Tran
  • 3,735
  • 1
  • 21
  • 29