Angular 2/4/5-动态设置基本href


130

我们有一个企业应用程序,其客户端使用Angular 2。我们每个客户都有自己独特的url,例如:https://our.app.com/customer-onehttps://our.app.com/customer-two。目前,我们可以使用来<base href...>动态设置document.location。因此,用户点击https://our.app.com/customer-two<base href...>设置为/customer-two“完美”!

问题是,例如,如果用户位于,https://our.app.com/customer-two/another-page并且他们刷新页面或尝试直接访问该URL,则将<base href...>设置为/customer-two/another-page,而找不到资源。

我们尝试了以下方法,但均无济于事:

<!doctype html>
<html>

<head>
  <script type='text/javascript'>
    var base = document.location.pathname.split('/')[1];
    document.write('<base href="https://stackoverflow.com/' + base + '" />');
  </script>

...

不幸的是,我们没有新主意。任何帮助都将是惊人的。


1
您是否尝试过使用APP_BASE_HREF进行任何操作?我不太确定如何实施,但似乎很有用……
Jarod Moser

当然,这当然也取决于您当前使用的路由器版本。您是否在路由器版本3.0.0-alpha.x上?
Jarod Moser

我正在使用“ @ angular / router”:“ 3.0.0-alpha.7”
sharpmachine

5
Angular 7-2018年11月。这仍然是一个问题。当前的答案提供了部分解决方案。在角度模块内做事为时已晚。在提供索引HTML时,它需要知道从何处提取脚本。我只有两种看待方式-使用asp \ php \ whatever来为index.html提供动态的basehref内容。或直接在index.html文件上使用本机JavaScript来更改DOM内容,然后再更改角度。两者都是解决常见问题的较差解决方案。伤心。
Stavm

1
有人也可以回答我的问题吗?stackoverflow.com/questions/53757931/…–
Karan

Answers:


166

此处标记的答案不能解决我的问题,可能是不同的角度版本。我可以angular cli在终端机/外壳程序中使用以下命令来实现所需的结果:

ng build --base-href /myUrl/

ng build --bh /myUrl/ 要么 ng build --prod --bh /myUrl/

这改变了<base href="https://stackoverflow.com/"><base href="https://stackoverflow.com/myUrl/">建的版本只这是非常适合我们环境的开发和生产之间的变化。最好的部分是没有代码库要求使用此方法进行更改。


总而言之,将index.html基本href 保留为:<base href="https://stackoverflow.com/">然后ng build --bh ./使用angular cli使其成为相对路径,或将其替换为./所需的内容。


更新:

如上面的示例显示了如何从命令行执行此操作,这是如何将其添加到angular.json配置文件中。

这将全部ng serving用于本地开发

"architect": {
    "build": {
      "builder": "@angular-devkit/build-angular:browser",
      "options": {
        "baseHref": "/testurl/",

这是特定于配置构建的配置,例如prod:

"configurations": {
   "Prod": {
     "fileReplacements": [
        {
          "replace": src/environments/environment.ts",
          "with": src/environments/environment.prod.ts"
        }
      ],
      "baseHref": "./productionurl/",

有关用法的官方 angular-cli文档。


35
该解决方案需要按客户构建,这可能无法解决OP的问题。
Maxime Morin

1
到目前为止,以我的经验,@ MaximeMorin可以在./所有位置使用。因此,实际上我认为您不必为每个客户构建。
Zze

9
@Zze我的经历./非常不同。直到用户导航到某条路线并按下浏览器的刷新按钮或按键,它才起作用。那时,我们的重写处理了调用,为索引页面提供了服务,但是浏览器假定这./是当前路由,而不是Web应用程序的根文件夹。我们通过设置基本href的动态(.aspx,.php,.jsp等)索引解决了OP的问题。
Maxime Morin

2
--prod在构建过程中使用不会改变您的base href价值,您不应在此处谈论它。--prod告诉angular-cli使用environment.prod.ts的文件,而不是默认的文件environment.ts你的environments文件夹
马修宙

1
@MattewEon这只是表明它与该--prod标志兼容,因为他们正在谈论OP中的部署。
Zze

57

这就是我们最终要做的。

将此添加到index.html。这应该是本<head>节中的第一件事

<base href="https://stackoverflow.com/">
<script>
  (function() {
    window['_app_base'] = '/' + window.location.pathname.split('/')[1];
  })();
</script>

然后在app.module.ts文件中,添加{ provide: APP_BASE_HREF, useValue: window['_app_base'] || '/' }到提供程序列表,如下所示:

import { NgModule, enableProdMode, provide } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { APP_BASE_HREF, Location } from '@angular/common';


import { AppComponent, routing, appRoutingProviders, environment } from './';

if (environment.production) {
  enableProdMode();
}

@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    HttpModule,
    routing],
  bootstrap: [AppComponent],
  providers: [
    appRoutingProviders,
    { provide: APP_BASE_HREF, useValue: window['_app_base'] || '/' },
  ]
})
export class AppModule { }

错误TS7017:元素隐式具有“ any”类型,因为类型“ Window”没有索引签名。useValue: (window as any) ['_app_base'] || '/'帮助
Erkan Buelbuel

对于最新的ng2真的不起作用,有解决方案吗?(加载错误的脚本路径,然后加载良好的脚本路径,但不在移动设备上加载)
Amit 2016年

1
由于内容长度过长,无法在评论中添加它作为答案。
克里希南

1
@Amit请在下面的最新答案中查看我的答案,以轻松解决问题。
Zze

3
您如何解决其他文件的问题。我的浏览器正在尝试加载localhost:8080 / main.bundle.js,但是不能,因为我的contextPath不同吗?(它应该获取localhost:8080 / hello / main.bundle.js。
Sasa

33

根据您(@sharpmachine)的答案,我能够对其进行一些重构,因此我们不必在中破解某个功能index.html

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { APP_BASE_HREF, Location } from '@angular/common';

import { AppComponent } from './';
import { getBaseLocation } from './shared/common-functions.util';

@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    HttpModule,
  ],
  bootstrap: [AppComponent],
  providers: [
    appRoutingProviders,
    {
        provide: APP_BASE_HREF,
        useFactory: getBaseLocation
    },
  ]
})
export class AppModule { }

并且,./shared/common-functions.util.ts包含:

export function getBaseLocation() {
    let paths: string[] = location.pathname.split('/').splice(1, 1);
    let basePath: string = (paths && paths[0]) || 'my-account'; // Default: my-account
    return '/' + basePath;
}

1
我已将myapp部署到子文件夹。我的网址现在看起来像这样: http://localhost:8080/subfolder/myapp/#/subfolder/myroute这是您的样子吗?您是否知道如何删除#之后的子文件夹,以便使其成为可能http://localhost:8080/subfolder/myapp/#/myroute
techguy2000

哦。我认为href基仅是LocationStrategy所必需的。就我而言,我正在使用HashLocationStrategy。所以我什至不需要设置它。您是否编写了特殊的代码来处理LocationStrategy的刷新?
techguy2000

1
是的,base href仅对于LocationStrategyAFAIK 是必需的。至于编写特殊的代码以使用LocationStrategy处理刷新,则不确定您的确切含义。您是在问,在我的应用程序活动期间,我的“基本href”会更改吗?然后,是的,我不得不做一些肮脏的事情,例如window.location.href = '/' + newBaseHref;需要更改的地方。我对此并不感到骄傲。另外,为了提供更新,我不再使用应用程序中的这些黑客解决方案,因为这对我来说是一个设计问题。路由器参数工作得很好,所以我坚持了下来。
克里希南

1
appRoutingProvider克里希南,你长什么样?我看到接受的答案使用了相同的答案;也许您只是复制了他的内容providers,但如果没有,您可以给我一个示例或描述其目的吗?该文档仅是imports路由模块,它不使用任何与路由相关的提供程序(据我所知)
Red Pea

@Krishnan以下是我与nginx location /subfolder/myapp/ { try_files $uri /subfolder/myapp/index.html; } location /subfolder2/myapp/ { try_files $uri /subfolder2/myapp/index.html; }
关联的功能

24

./在同一域之外构建多个应用程序时,我使用当前工作目录:

<base href="./">

顺便提一下,我.htaccess用来协助页面重新加载的路由:

RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule .* index.html [L]

2
非常感谢您提供的.htaccess文件
Shahriar Siraj Snigdho

8
此答案不正确,不适用于/ your-app / a / b / c之类的路由。如果从这种方式刷新页面,Angular将在/ your-app / a / b /下查找运行时,polyfills和主要JS文件以及样式CSS文件。他们显然不在该位置,而是在/ your-app /下
toongeorges

2
@toongeorges这个答案是大约两年前写的,它使用Apache Web Server软件检测并执行的配置文件来处理页面重新加载时的路由。可以说这个解决方案是不正确的,不起作用会误导您,但是您的意思是您无法弄清楚如何使其与您的配置一起使用。
Daerik '18年

我忽略了Apache重写规则,但我对此知之甚少,因此无法在Apache上运行,因此我可能是错的,您的答案可能是正确的。尽管无论如何我都更喜欢一种不需要您修改服务器配置的解决方案,因为这里给出了多个答案,因为这使该应用程序可跨不同的服务器移植。
toongeorges

2
不,@ toongeorges是正确的,将其更改为<base href="./">是错误的,并且会像/app/a/b/c实际那样通过测试来搞砸。如果只与Web应用程序打交道,则最好自己设置基本href,或者在部署时看看更改基本href的角度方式(这里有很多答案)。
giovannipds

15

现有的简化答案@sharpmachine

import { APP_BASE_HREF } from '@angular/common';
import { NgModule } from '@angular/core';

@NgModule({
    providers: [
        {
            provide: APP_BASE_HREF,
            useValue: '/' + (window.location.pathname.split('/')[1] || '')
        }
    ]
})

export class AppModule { }

如果要提供APP_BASE_HREF不透明令牌的值,则不必在index.html中指定基本标记。


10

这在产品环境中对我来说很好

<base href="https://stackoverflow.com/" id="baseHref">
<script>
  (function() {
    document.getElementById('baseHref').href = '/' + window.location.pathname.split('/')[1] + "/";
  })();
</script>

2
对于IIS中的多个VD设置,此解决方案有很大的局限性。这会导致重复加载块。
卡兰

9

在angular4中,您还可以在.angular-cli.json应用程序中配置baseHref。

在.angular-cli.json中

{   ....
 "apps": [
        {
            "name": "myapp1"
            "baseHref" : "MY_APP_BASE_HREF_1"
         },
        {
            "name": "myapp2"
            "baseHref" : "MY_APP_BASE_HREF_2"
         },
    ],
}

这会将index.html中的基本href更新为MY_APP_BASE_HREF_1

ng build --app myapp1

4
这也需要为每个应用构建一个版本。
帕特里克·希尔特

6

如果您使用的是Angular CLI 6,则可以在angular.json中使用deployUrl / baseHref选项(项目> xxx>架构师>构建>配置>生产)。这样,您可以轻松地为每个项目指定baseUrl。

通过查看内置应用程序的index.html来检查设置是否正确。


7
这就要求每个环境都有一个不同的版本,及其所有含义。
Stavm '18

3

附加组件:如果您想要例如:/ users作为路由器的应用程序基础,/ public作为资产使用的基础

$ ng build -prod --base-href /users --deploy-url /public

1

遇到了类似的问题,实际上我最终是在研究网络中是否存在某些文件。

probePath将是您要检查的文件的相对URL(如果您现在位于正确的位置,则为一种标记),例如'assets / images / thisImageAlwaysExists.png'

<script type='text/javascript'>
  function configExists(url) {
    var req = new XMLHttpRequest();
    req.open('GET', url, false); 
    req.send();
    return req.status==200;
  }

  var probePath = '...(some file that must exist)...';
  var origin = document.location.origin;
  var pathSegments = document.location.pathname.split('/');

  var basePath = '/'
  var configFound = false;
  for (var i = 0; i < pathSegments.length; i++) {
    var segment = pathSegments[i];
    if (segment.length > 0) {
      basePath = basePath + segment + '/';
    }
    var fullPath = origin + basePath + probePath;
    configFound = configExists(fullPath);
    if (configFound) {
      break;
    }
  }

  document.write("<base href='" + (configFound ? basePath : '/') + "' />");
</script>

-1

坦白说,您的情况对我来说很好!

我正在使用Angular 5。

<script type='text/javascript'>
    var base = window.location.href.substring(0, window.location.href.toLowerCase().indexOf('index.aspx'))
    document.write('<base href="' + base + '" />');
</script>

这个解决方案对您有用吗?我正面临这个问题。您能否也回复我的帖子。stackoverflow.com/questions/53757931/…–
卡兰(Karan)

1
请避免使用document.write():developers.google.com/web/updates/2016/08/...
帕特里克Hillert

-5

我只是改变了:

  <base href="/">

对此:

  <base href="/something.html/">

不要忘记以/结尾

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.