Angular 2/4/5 - динамически установить базовый href


131

У нас есть корпоративное приложение, которое использует Angular 2 для клиента. У каждого из наших клиентов есть свой уникальный URL-адрес, например: https://our.app.com/customer-oneи https://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 ? Я не совсем уверен, как это будет реализовано, но, похоже, это было бы полезно ...
Джарод Мозер

Конечно, это определенно зависит от того, какую версию маршрутизатора вы используете в настоящее время. Вы используете роутер версии 3.0.0-alpha.x?
Джарод Мозер

Я использую "@ angular / router": "3.0.0-alpha.7"
sharpmachine

5
Angular 7 - ноябрь 2018 г. это все еще проблема. текущие ответы дают частичное решение. делать вещи внутри модуля angular слишком поздно. поскольку ваш индексный HTML-код обслуживается, ему необходимо знать, откуда брать скрипты. есть только два способа, как я это вижу: использовать asp \ php \ something для обслуживания index.html с динамическим содержимым basehref. или используя собственный javascript прямо в файле index.html, чтобы изменить содержимое DOM до того, как оно станет angular. оба являются плохим решением общей проблемы. печальный.
Stavm 08

1
Может кто-нибудь также ответит на мой вопрос. stackoverflow.com/questions/53757931/…
Каран

Ответы:


166

Отмеченный здесь ответ не решил мою проблему, возможно, разные угловые версии. Мне удалось добиться желаемого результата с помощью следующей angular cliкоманды в терминале / оболочке:

ng build --base-href /myUrl/

ng build --bh /myUrl/ или ng build --prod --bh /myUrl/

Это изменяет <base href="https://stackoverflow.com/">To <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.
Максим Морин

1
@MaximeMorin, по моему опыту, ./работает для всех мест. На самом деле я не думаю, что вам нужно создавать для каждого клиента.
Zze

9
@Zze Мой опыт ./был совсем другим. Он работает до тех пор, пока пользователь не перейдет к маршруту и ​​не нажмет кнопку или клавиши обновления браузера. В этот момент наши перезаписи обрабатывают вызов, обслуживают страницу индекса, но браузер предполагает, что ./это текущий маршрут, а не корневая папка веб-приложения. Мы решили проблему OP с помощью динамического индекса (.aspx, .php, .jsp и т. Д.), Который устанавливает базовый href.
Максим Морин

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: элемент неявно имеет тип «любой», поскольку тип «Окно» не имеет подписи индекса. useValue: (window as any) ['_app_base'] || '/'помогает
Erkan Buelbuel

Не реально на последней ng2 работает, есть решение? (загружает путь к плохим сценариям, затем путь к хорошим сценариям, но не на мобильных устройствах)
Амит

1
Добавьте его в качестве ответа ниже, так как длина контента слишком велика для комментария.
Кришнан

1
@Amit Пожалуйста, посмотрите мой ответ ниже, чтобы легко решить проблему в новейшей версии ng2.
Zze 07

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
У меня мое приложение развернуто во вложенной папке. Теперь мой URL-адрес выглядит так: http://localhost:8080/subfolder/myapp/#/subfolder/myrouteА ваш? Вы знаете, как избавиться от подпапки после #, чтобы она могла быть просто http://localhost:8080/subfolder/myapp/#/myroute?
techguy2000 05

Ой. Я думаю, что база href требуется только для LocationStrategy. В моем случае я использую HashLocationStrategy. Так что мне даже не нужно его устанавливать. Приходилось ли вам писать специальный код для обработки обновления с помощью LocationStrategy?
techguy2000 05

1
Да, base hrefтребуется только для LocationStrategyAFAIK. Что касается написания специального кода для обработки обновления с помощью LocationStrategy, не уверен, что именно вы имеете в виду. Вы спрашиваете, что, если мой "базовый href" изменится во время действия в моем приложении? Тогда да, мне пришлось сделать грязную вещь, например, window.location.href = '/' + newBaseHref;где это требовалось изменить. Я не слишком горжусь этим. Кроме того, чтобы дать обновление, я отказался от этих хакерских решений в своем приложении, поскольку это становилось проблемой для меня. Параметры маршрутизатора работают отлично, поэтому я придерживался их.
Кришнан

1
Как ты appRoutingProviderвыглядишь, Кришнан? Я вижу, что в принятом ответе использовалось то же самое; может быть, вы просто скопировали его providers, но если нет, можете ли вы дать мне образец или описать его назначение? В документации только importsмодули маршрутизации , она не использует каких-либо поставщиков, связанных с маршрутизацией (насколько я понимаю)
The Red Pea

@Krishnan Вот что мне нужно сделать с nginx, чтобы он заработал:location /subfolder/myapp/ { try_files $uri /subfolder/myapp/index.html; } location /subfolder2/myapp/ { try_files $uri /subfolder2/myapp/index.html; }
techguy2000,

24

Я использую текущий рабочий каталог ./при создании нескольких приложений из одного домена:

<base href="./">

Кстати, я использую, .htaccessчтобы помочь с маршрутизацией при перезагрузке страницы:

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

2
Большое спасибо за .htaccessфайл
Шахриар Сирадж Снигдхо

8
Этот ответ неверен, он не работает для таких маршрутов, как / your-app / a / b / c. Если вы обновите свою страницу с помощью такого маршрута, Angular будет искать среду выполнения, полифиллы и основные файлы JS и файл CSS стилей в / your-app / a / b /. Очевидно, они не в этом месте, а в / your-app /
toongeorges

2
@toongeorges Этот ответ был написан почти два года назад с использованием файла конфигурации, который обнаруживается и запускается программным обеспечением веб-сервера Apache для обработки маршрутизации при перезагрузке страницы. То, что вы говорите, что это решение неверно и не работает, вводит в заблуждение, но вы имеете в виду, что не могли понять, как заставить его работать с вашей конфигурацией.
Daerik

Я пропустил правило перезаписи Apache, и я недостаточно знаю о нем, чтобы сказать, что это не будет работать на Apache, поэтому я могу ошибаться и ваш ответ может быть правильным. Хотя в любом случае я предпочитаю решение, которое не требует от вас изменения конфигурации сервера, поскольку здесь было дано несколько ответов, потому что это делает приложение переносимым на разные серверы.
toongeorges

2
Нет, @toongeorges прав, изменение на <base href="./">неверно и испортит маршруты, как и в /app/a/b/cсамом деле, только что протестировал. Предпочтите устанавливать базовый 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 { }

Вам не нужно указывать базовый тег в вашем index.html, если вы указываете значение для непрозрачного токена APP_BASE_HREF.


10

Это отлично работает для меня в среде prod

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

2
Это решение имеет большое ограничение, связанное с настройкой нескольких виртуальных дисков в IIS. Это вызывает двойную загрузку фрагментов.
Karan

9

В angular4 вы также можете настроить baseHref в приложениях .angular-cli.json.

в .angular-cli.json

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

это обновит базовый href в index.html до MY_APP_BASE_HREF_1

ng build --app myapp1

4
Это также должно сделать сборку для каждого приложения.
Патрик

6

Если вы используете Angular CLI 6, вы можете использовать параметры deployUrl / baseHref в angular.json (проекты> xxx> архитектор> сборка> конфигурации> производство). Таким образом, вы можете легко указать baseUrl для каждого проекта.

Убедитесь, что ваши настройки верны, просмотрев index.html созданного приложения.


7
это требует отдельной сборки для каждой среды со всеми вытекающими последствиями.
Stavm 08

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/…
Каран

1
Пожалуйста, избегайте использования document.write (): developers.google.com/web/updates/2016/08/…
Патрик

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.