Кутовий 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 ? Я не зовсім впевнений, як це було б реалізовано, але, здається, це було б корисно ...
Jarod Moser

Звичайно, це безумовно залежить від того, яку версію маршрутизатора ви також використовуєте. Ви перебуваєте на маршрутизаторі версії 3.0.0-alpha.x?
Jarod Moser

Я використовую "@ angular / router": "3.0.0-alpha.7"
sharmachine

5
Кутова 7 - листопад 2018 року. Це все ще проблема. поточні відповіді дають часткове рішення. робити речі всередині кутового модуля занадто пізно. Оскільки HTML вашого індексу подається, він повинен знати, звідки брати скрипти. Є лише два способи, як я це бачу - використовуючи asp \ php \ що б не обслуговувати index.html з динамічним вмістом basehref. або використовуючи нативний JavaScript прямо у файлі index.html, щоб змінити вміст DOM до того, як він перейде до кутового. обидва є поганим рішенням поширеної проблеми. сумно.
Ставм

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/">для <base href="https://stackoverflow.com/myUrl/">в побудованої версію тільки що було ідеально підходить для нашого зміни навколишнього середовища між розвитком і виробництвом. Найкраще, що кодова база не потребує змін за допомогою цього методу.


Підводячи підсумки, залиште своє index.html базовий href таким чином: <base href="https://stackoverflow.com/">потім запустіть 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/",

Офіційна angular-cli документація зі посиланням на використання.


35
Це рішення вимагає складання на кожного замовника, що, ймовірно, не вирішує проблему ОП.
Максим Морін

1
@MaximeMorin, з мого досвіду, що зараз це робить, ./працює для всіх локацій. Так що насправді я не думаю, що ви повинні будувати кожного клієнта.
Zze

9
@Zze Мій досвід роботи ./був дуже різний. Він працює, поки користувач не перейде на маршрут і не натисне кнопку оновлення або клавіші оновлення браузера. У цей момент наші переписувачі обробляють дзвінок, обслуговують індексну сторінку, але браузер припускає, що ./поточний маршрут не є кореневою папкою веб-програми. Ми вирішили проблему ОП динамічним (.aspx, .php, .jsp тощо) індексом, який встановлює базовий href.
Максим Морін

2
Використання --prodпід час збирання не змінить ваше base hrefзначення, тут не варто говорити про це. --prodговорить angular-cliвикористовувати environment.prod.tsфайл замість файлу за замовчуванням environment.tsу вашій environmentsпапці
Mattew Eon

1
@MattewEon Це просто демонструвало, що він сумісний із --prodпрапором, оскільки вони говорили про розгортання в ОП.
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'] || '/'допомагає
Еркан Буелбуель

Насправді не працює останній ng2, чи є рішення? (завантажує поганий шлях сценаріїв, потім хороший сценарій шлях, але не на мобільному)
Amit

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

1
@Amit Будь ласка, дивіться мою відповідь нижче для легкого вирішення в найновішому ng2.
Zze

3
Як ви вирішили проблему з іншими файлами. Мій веб-переглядач намагається завантажити localhost: 8080 / main.bundle.js , але це не може, оскільки мій contextPath інший? (Це має бути виїзний localhost: 8080 / hello / main.bundle.js. )
Саса

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 для підпапки. Моя URL-адреса виглядає приблизно так: 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маршрутизації модулів , вона не використовує маршрутизацію , пов'язану з постачальників (наскільки я можу судити)
Червоний Горох

@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 Web Server для обробки маршрутизації під час перезавантаження сторінки. Ви можете сказати, що це рішення є невірним і не працює, вводить в оману, але ви маєте на увазі те, що ви не могли зрозуміти, як змусити його працювати з вашою конфігурацією.
Даерік

Я не помітив правила перезапису 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
Це рішення має велике обмеження при множинніх налаштуваннях VD в IIS. Це викликає повторне завантаження шматок.
Каран

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, ви можете використовувати параметри implemenUrl / baseHref в angular.json (проекти> xxx> архітектор> збірка> конфігурації> виробництво). Таким чином, ви можете легко вказати baseUrl на проект.

Перевірте правильність налаштувань, переглянувши index.html вбудованого додатка.


7
це вимагає різної побудови для кожного середовища з усіма наслідками.
Ставм

3

Додатки: Якщо ви хочете, наприклад: / користувачі як база додатків для маршрутизатора та / public як база для використання активів

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

1

У мене була подібна проблема, насправді я закінчився дослідженням існування якогось файлу в моєму Інтернеті.

probePath буде відносною URL-адресою до файлу, який ви хочете перевірити (вид маркера, якщо ви зараз перебуваєте у правильному місці), наприклад, "активи / зображення / 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.