Angular 2+ - 动态设置基本href

Posted

技术标签:

【中文标题】Angular 2+ - 动态设置基本href【英文标题】:Angular 2+ - Set base href dynamically 【发布时间】:2016-11-01 22:42:32 【问题描述】:

我们有一个为客户端使用 Angular 2 的企业应用程序。我们的每个客户都有自己独特的网址,例如:https://our.app.com/customer-onehttps://our.app.com/customer-two。目前我们可以使用document.location动态设置<base href...>。所以用户点击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="/' + base + '" />');
  </script>

...

不幸的是,我们还没有新的想法。任何帮助都会很棒。

【问题讨论】:

你有没有尝试过使用APP_BASE_HREF?我不太确定它将如何实现,但它似乎是有用的...... 当然这也取决于您当前使用的路由器版本。你在路由器版本 3.0.0-alpha.x 上吗? 我正在使用“@angular/router”:“3.0.0-alpha.7” Angular 7 - 2018 年 11 月。这仍然是一个问题。当前的答案提供了部分解决方案。在 Angular 模块中做事为时已晚。当您的索引 HTML 被提供时,它需要知道从哪里提取脚本。我看到的方式只有 2 种方式 - 使用 asp\php\whatever 为 index.html 提供动态 basehref 内容。或在 index.html 文件上使用本机 javascript 在 DOM 内容变为 Angular 之前对其进行更改。两者都是对一个共同问题的糟糕解决方案。伤心。 有人也可以回答我的问题吗? ***.com/questions/53757931/… 【参考方案1】:

您可以使用以下方法在 app.module.ts 文件中动态设置基本 href:https://angular.io/api/common/APP_BASE_HREF

import Component, NgModule from '@angular/core';
import APP_BASE_HREF from '@angular/common';

@NgModule(
  providers: [provide: APP_BASE_HREF, useValue: '/my/app']
)
class AppModule 

【讨论】:

【参考方案2】:

在 package.json 中将标志 --base-href 设置为相对路径:

"script": 
    "build": "ng build --base-href ./"

【讨论】:

【参考方案3】:

我刚刚换了:

  <base href="/">

到这里:

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

别忘了以 / 结尾

【讨论】:

【参考方案4】:

有一个类似的问题,实际上我最终在我的网络中探测了一些文件的存在。

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>

【讨论】:

【参考方案5】:

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

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

【讨论】:

【参考方案6】:

此处标记的答案没有解决我的问题,可能是不同的角度版本。我能够在终端/shell 中使用以下angular cli 命令实现预期的结果:

ng build --base-href /myUrl/

ng build --bh /myUrl/ng build --prod --bh /myUrl/

这会将仅内置版本中的&lt;base href="/"&gt; 更改为&lt;base href="/myUrl/"&gt;,这非常适合我们在开发和生产之间更改环境。最好的部分是没有代码库需要使用这种方法进行更改。


总而言之,将您的 index.html 基本 href 保留为:&lt;base href="/"&gt; 然后使用 angular cli 运行 ng build --bh ./ 以使其成为相对路径,或者将 ./ 替换为您需要的任何内容。

更新:

如上例所示,如何从命令行执行此操作,以下是如何将其添加到您的 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/",

officialangular-cli 文档涉及使用情况。

【讨论】:

此解决方案需要为每个客户构建,这可能无法解决 OP 的问题。 @MaximeMorin 以我目前的经验来看,./ 适用于所有地点。所以实际上我认为你不必为每个客户构建。 @Zze 我对./ 的体验非常不同。它一直有效,直到用户导航到路线并点击浏览器的刷新按钮或按键。此时,我们的重写处理调用,提供索引页面,但浏览器假定./ 是当前路由而不是 Web 应用程序的根文件夹。我们通过设置基本 href 的动态(.aspx、.php、.jsp 等)索引解决了 OP 的问题。 在构建过程中使用--prod 不会改变你的base href 值,你不应该在这里谈论它。 --prod 告诉 angular-cli 使用 environment.prod.ts 文件而不是 environments 文件夹中的默认文件 environment.ts Angular 7.1:Unknown option: '--bh'--base-href 工作。【参考方案7】:

坦率地说,你的情况对我来说很好!

我正在使用 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>

【讨论】:

这个解决方案对你有用吗?我正面临这个问题。能否请您也回复我的帖子。 ***.com/questions/53757931/… 请避免使用 document.write(): developers.google.com/web/updates/2016/08/…【参考方案8】:

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

通过查看已构建应用的 index.html 来检查您的设置是否正确。

【讨论】:

这需要针对每个环境进行不同的构建,包括所有影响。【参考方案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

【讨论】:

这也需要为每个app做一个build。【参考方案10】:

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

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

【讨论】:

此解决方案在 IIS 中设置多个 VD 时存在很大限制。它会导致重复加载块。 使用硬编码的 base-href 并调用 http://my.app.com/app1 将 url 更改为 http://my.app.com/app1/ (最后 / )这个解决方案在 http://my.app.com/app1 的情况下也适用于我,因为更改为 http://my.app.com/app1/ - 否则无法加载资源。【参考方案11】:

将现有的answer 简化为@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 中指定基本标记。

【讨论】:

【参考方案12】:

根据您 (@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;

【讨论】:

我已将 myapp 部署到子文件夹。我的网址现在看起来像这样:http://localhost:8080/subfolder/myapp/#/subfolder/myroute 这是你的样子吗?你知道如何去掉 # 之后的子文件夹,让它变成http://localhost:8080/subfolder/myapp/#/myroute吗? 哦。我认为 href base 只有 LocationStrategy 才需要。就我而言,我使用的是 HashLocationStrategy。所以我什至不需要设置它。您是否编写了特殊代码来处理 LocationStrategy 的刷新? 是的,base href 仅适用于 LocationStrategy AFAIK。至于使用 LocationStrategy 编写特殊代码来处理刷新,不确定您的确切意思。您是在问,如果我的“基本 href”在我的应用程序中的活动期间发生变化怎么办?然后,是的,我不得不做一件像window.location.href = '/' + newBaseHref; 这样需要更改的肮脏事情。我对此并不感到骄傲。此外,为了提供更新,我在我的应用程序中放弃了这些黑客解决方案,因为它已成为我的设计问题。路由器参数工作得很好,所以我坚持使用它。 你的appRoutingProvider 是什么样的,克里希南?我看到接受的答案是一样的;也许你只是复制了他的providers,但如果没有,你能给我一个样本或描述它的用途吗? documentation only imports routing modules,它不使用任何与路由相关的提供程序(据我所知) @Krishnan 以下是我必须使用 nginx 做的事情:location /subfolder/myapp/ try_files $uri /subfolder/myapp/index.html; location /subfolder2/myapp/ try_files $uri /subfolder2/myapp/index.html; 【参考方案13】:

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

<base href="./">

附带说明,我使用.htaccess 来协助我在页面重新加载时路由:

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

【讨论】:

非常感谢您提供.htaccess 文件 这个答案不正确,它不适用于/your-app/a/b/c 等路由。如果您从这样的路由刷新页面,Angular 将在 /your-app/a/b/ 下查找运行时、polyfills 和主 JS 文件和样式 CSS 文件。他们显然不在那个位置,而是在 /your-app/ @toongeorges 这个答案是大约两年前写的,使用的配置文件由 Apache Web 服务器软件检测并执行,以处理页面重新加载时的路由。你说这个解决方案不正确并且不起作用是一种误导,但你的意思是你无法弄清楚如何让它与你的配置一起工作。 我忽略了 Apache 重写规则,我对此知之甚少,不能说这在 Apache 上不起作用,所以我可能是错的,你的答案可能是正确的。尽管在任何情况下我都更喜欢不需要您修改服务器配置的解决方案,因为这里已经给出了多个答案,因为这使得应用程序可以跨不同的服务器移植。 使用./ 会中断路由到多级深度的地址。例如http://localhost:4200/foo/bar.【参考方案14】:

这就是我们最终要做的事情。

将此添加到 index.html。应该是&lt;head&gt;部分的第一件事

<base href="/">
<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:元素隐式具有“任何”类型,因为类型“窗口”没有索引签名。 useValue: (window as any) ['_app_base'] || '/'helps 对于最新的ng2真的不行,有解决办法吗? (加载错误的脚本路径,然后加载好的脚本路径,但不在移动设备上) 将其添加为下面的答案,因为内容长度太长,无法发表评论。 @Amit 请在下面查看我的答案,以便在最新的 ng2 中轻松解决。 您是如何解决其他文件的问题的。我的浏览器正在尝试加载localhost:8080/main.bundle.js,但因为我的 contextPath 不同而无法加载? (应该是获取localhost:8080/hello/main.bundle.js.)

以上是关于Angular 2+ - 动态设置基本href的主要内容,如果未能解决你的问题,请参考以下文章

打字稿 | JavaScript | Angular 2:动态设置@HostListener 参数

Angular 2 动态嵌套表单

angular动态设置disable

使用 Angular 2 的动态标签导航系统

带有动态模板或 templateUrl 的 Angular 2/4 组件

在 Angular 2 中编写最基本的单元测试?