Kątowy 2/4/5 - dynamiczne ustawianie podstawowego href

131

Mamy aplikację dla przedsiębiorstw, która korzysta z Angular 2 dla klienta. Każdy z naszych klientów ma swój unikalny adres URL, np .: https://our.app.com/customer-onei https://our.app.com/customer-two. Obecnie jesteśmy w stanie ustawić <base href...>dynamicznie za pomocą document.location. Więc użytkownik trafia https://our.app.com/customer-twoi <base href...>zostaje ustawiony na /customer-two... idealny!

Problem polega na tym, że jeśli użytkownik jest na przykład pod adresem https://our.app.com/customer-two/another-pagei odświeża stronę lub próbuje bezpośrednio trafić na ten adres URL, <base href...>zostaje ustawiony na /customer-two/another-pagei nie można znaleźć zasobów.

Bezskutecznie próbowaliśmy następujących rozwiązań:

<!doctype html>
<html>

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

...

Niestety, brakuje nam pomysłów. Każda pomoc byłaby niesamowita.

Sharpmachine
źródło
1
Czy próbowałeś cokolwiek z APP_BASE_HREF ? Nie jestem do końca pewien, jak zostanie to wdrożone, ale wydaje się, że byłoby to przydatne ...
Jarod Moser,
Oczywiście na pewno zależy to również od wersji routera, z której obecnie korzystasz. Czy korzystasz z routera w wersji 3.0.0-alpha.x?
Jarod Moser
Używam „@ angular / router”: „3.0.0-alpha.7”
sharpmachine,
5
Angular 7 - listopad 2018. to wciąż problem. obecne odpowiedzi zapewniają częściowe rozwiązanie. robienie rzeczy wewnątrz modułu kątowego jest za późno. gdy obsługiwany jest indeks HTML, musi wiedzieć, skąd pobrać skrypty. widzę to tylko na dwa sposoby - używając asp \ php \ cokolwiek do obsługi pliku index.html z dynamiczną zawartością basehref. lub używając natywnego javascript bezpośrednio w pliku index.html, aby zmienić zawartość DOM, zanim stanie się kątowa. obie są kiepskimi rozwiązaniami wspólnego problemu. smutny.
Stavm
1
Czy ktoś może również odpowiedzieć na moje zapytanie. stackoverflow.com/questions/53757931/…
Karan

Odpowiedzi:

166

Zaznaczona tutaj odpowiedź nie rozwiązała mojego problemu, być może różne wersje kątowe. Udało mi się osiągnąć pożądany wynik za pomocą następującego angular clipolecenia w terminalu / powłoce:

ng build --base-href /myUrl/

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

Zmienia to <base href="https://stackoverflow.com/">tylko <base href="https://stackoverflow.com/myUrl/">w wersji zbudowanej, która była idealna dla naszej zmiany środowiska między programowaniem a produkcją. Najlepsze było to, że żadna baza kodu nie wymaga zmiany za pomocą tej metody.


Podsumowując, zostaw swój index.htmlpodstawowy href jako: <base href="https://stackoverflow.com/">następnie uruchom ng build --bh ./z kątowym kliknięciem, aby uczynić go ścieżką względną, lub zastąp to, ./czego potrzebujesz.


Aktualizacja:

Ponieważ powyższy przykład pokazuje, jak to zrobić z wiersza poleceń, oto jak dodać go do pliku konfiguracyjnego angular.json.

Zostanie to wykorzystane dla wszystkich na ng servingpotrzeby rozwoju lokalnego

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

To jest konfiguracja specyficzna dla kompilacji konfiguracyjnych, takich jak prod:

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

Oficjalna angular-cli dokumentacja odnosząc się do użytku.

Zze
źródło
35
To rozwiązanie wymaga kompilacji na klienta, co prawdopodobnie nie rozwiązuje problemu OP.
Maxime Morin
1
Z mojego doświadczenia wynika, że ​​@MaximeMorin ./działa do wszystkich lokalizacji. Więc właściwie nie sądzę, że musisz tworzyć dla każdego klienta.
Zze
9
@Zze Moje doświadczenia z ./były bardzo różne. Działa, dopóki użytkownik nie przejdzie do trasy i nie naciśnie przycisku lub klawiszy odświeżania przeglądarki. W tym momencie nasze przepisywanie obsługuje wywołanie, obsługuje stronę indeksu, ale przeglądarka przyjmuje, że ./jest to bieżąca trasa, a nie folder główny aplikacji internetowej. Rozwiązaliśmy problem z OP za pomocą dynamicznego indeksu (.aspx, .php, .jsp itp.), Który ustawia podstawowy element href.
Maxime Morin
2
Używanie --prodpodczas budowania nie zmieni twojej base hrefwartości, nie powinieneś o tym tutaj mówić. --prodmówi, angular-cliaby użyć environment.prod.tspliku zamiast domyślnego pliku environment.tsw environmentsfolderze
Mattew Eon
1
@MattewEon To właśnie pokazywało, że jest zgodne z --prodflagą, ponieważ rozmawiali o wdrożeniu w PO.
Zze
57

Oto, co ostatecznie zrobiliśmy.

Dodaj to do index.html. To powinna być pierwsza rzecz w <head>sekcji

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

Następnie w app.module.tspliku dodaj { provide: APP_BASE_HREF, useValue: window['_app_base'] || '/' }do listy dostawców, na przykład:

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 { }
Sharpmachine
źródło
Błąd TS7017: Element niejawnie ma typ „dowolny”, ponieważ typ „Okno” nie ma podpisu indeksu. useValue: (window as any) ['_app_base'] || '/'pomaga
Erkan Buelbuel
Tak naprawdę nie działa na najnowszym ng2, czy istnieje rozwiązanie? (ładuje ścieżkę złych skryptów, a następnie dobrą ścieżkę skryptów, ale nie na telefonie komórkowym)
Amit
1
Dodanie go jako odpowiedzi poniżej, ponieważ treść jest zbyt długa, aby można było komentować.
Krishnan,
1
@Amit Proszę zobaczyć moją odpowiedź poniżej, aby łatwo rozwiązać w najnowszym ng2.
Zze,
3
Jak rozwiązałeś problem z innymi plikami. Moja przeglądarka próbuje załadować localhost: 8080 / main.bundle.js , ale nie może, ponieważ moja ścieżka contextPath jest inna? (Powinno to być pobieranie localhost: 8080 / hello / main.bundle.js. )
Sasa
33

Na podstawie Twojej odpowiedzi (@sharpmachine) udało mi się ją nieco zmienić, aby nie musieliśmy hakować funkcji 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 { }

I ./shared/common-functions.util.tszawiera:

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;
}
Krishnan
źródło
1
Moja aplikacja została wdrożona w podfolderze. Mój adres URL wygląda teraz tak: http://localhost:8080/subfolder/myapp/#/subfolder/myrouteCzy tak wygląda Twój adres ? Czy wiesz, jak pozbyć się podfolderu po znaku #, aby mógł tak po prostu być http://localhost:8080/subfolder/myapp/#/myroute?
techguy2000
O. Myślę, że baza href jest wymagana tylko dla LocationStrategy. W moim przypadku używam HashLocationStrategy. Więc nawet nie muszę tego ustawiać. Czy napisałeś specjalny kod do obsługi odświeżania za pomocą LocationStrategy?
techguy2000
1
Tak, base hrefjest wymagane tylko w przypadku LocationStrategyAFAIK. Jeśli chodzi o pisanie specjalnego kodu do obsługi odświeżania za pomocą LocationStrategy, nie jestem pewien, co dokładnie masz na myśli. Pytasz, co się stanie, jeśli mój „podstawowy href” zmieni się podczas działania w mojej aplikacji? Wtedy, tak, musiałem zrobić brudną rzecz, na przykład window.location.href = '/' + newBaseHref;tam, gdzie trzeba było to zmienić. Nie jestem z tego zbyt dumna. Ponadto, aby zaktualizować, odszedłem od tych rozwiązań hakerskich w mojej aplikacji, ponieważ stawały się one dla mnie problemem projektowym. Parametry routera działają doskonale, więc trzymałem się tego.
Krishnan
1
Jak appRoutingProviderwyglądasz, Krishnan? Widzę, że zaakceptowana odpowiedź była taka sama; może właśnie skopiowałeś jego kopię providers, ale jeśli nie, czy możesz dać mi próbkę lub opisać jej przeznaczenie? Dokumentacja tylko importsrouting modułów , nie używa żadnych dostawców związanych routingu (o ile można widzę)
The Red Pea
@Krishnan Oto, co muszę zrobić z nginx, aby to działało:location /subfolder/myapp/ { try_files $uri /subfolder/myapp/index.html; } location /subfolder2/myapp/ { try_files $uri /subfolder2/myapp/index.html; }
techguy2000
24

Używam bieżącego katalogu roboczego ./podczas budowania kilku aplikacji z tej samej domeny:

<base href="./">

Na marginesie, .htaccesspomagam w routingu przy ponownym ładowaniu strony:

RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule .* index.html [L]
Daerik
źródło
2
Dziękuję bardzo za .htaccessplik
Shahriar Siraj Snigdho
8
Ta odpowiedź jest nieprawidłowa, nie działa w przypadku tras takich jak / twoja-aplikacja / a / b / c. Jeśli odświeżysz swoją stronę z takiej trasy, Angular poszuka środowiska uruchomieniowego, polyfillów i głównych plików JS oraz pliku CSS stylów w / twoja-aplikacja / a / b /. Oczywiście nie są w tej lokalizacji, ale pod / twoja-aplikacja /
toongeorges
2
@toongeorges Ta odpowiedź została napisana prawie dwa lata temu przy użyciu pliku konfiguracyjnego, który jest wykrywany i wykonywany przez oprogramowanie serwera internetowego Apache w celu obsługi routingu podczas ponownego ładowania strony. Twierdzenie, że to rozwiązanie jest niepoprawne i nie działa, jest mylące, ale masz na myśli to, że nie możesz dowiedzieć się, jak sprawić, aby działało z twoją konfiguracją.
Daerik
Przeoczyłem zasadę przepisywania Apache i nie wiem o niej wystarczająco dużo, aby powiedzieć, że to nie zadziała na Apache, więc mogę się mylić, a twoja odpowiedź może być poprawna. Chociaż w każdym razie wolę rozwiązanie, które nie wymaga modyfikacji konfiguracji serwera, ponieważ udzielono tutaj wielu odpowiedzi, ponieważ dzięki temu aplikacja jest przenośna na różnych serwerach.
toongeorges
2
Nie, @toongeorges ma rację, zmiana na <base href="./">jest nieprawidłowa i spieprzy z trasami, tak jak /app/a/b/crzeczywiście, właśnie to przetestowałem. Jeśli masz do czynienia tylko z aplikacją internetową, wolisz samodzielnie ustawić podstawowy atrybut href lub przyjrzeć się kątowemu sposobowi zmiany podstawowego elementu href podczas wdrażania (jest tu wiele odpowiedzi, w których wspomina się o tym).
giovannipds,
15

Uproszczenie istniejącej odpowiedzi przez @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 { }

Nie musisz określać znacznika podstawowego w swoim index.html, jeśli podajesz wartość dla nieprzezroczystego tokenu APP_BASE_HREF.

Karanvir Kang
źródło
10

To działa dobrze dla mnie w środowisku produkcyjnym

<base href="https://stackoverflow.com/" id="baseHref">
<script>
  (function() {
    document.getElementById('baseHref').href = '/' + window.location.pathname.split('/')[1] + "/";
  })();
</script>
Avikar
źródło
2
To rozwiązanie ma duże ograniczenie z wieloma konfiguracjami VD w usługach IIS. Powoduje zduplikowane ładowanie fragmentów.
Karan
9

W angular4 można również skonfigurować baseHref w aplikacjach .angular-cli.json.

w .angular-cli.json

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

spowoduje to zaktualizowanie podstawowego href w index.html do MY_APP_BASE_HREF_1

ng build --app myapp1
Vince
źródło
4
To również musi stworzyć kompilację dla każdej aplikacji.
Patrick Hillert,
6

Jeśli używasz Angular CLI 6, możesz użyć opcji deployUrl / baseHref w angular.json (projekty> xxx> architekt> ​​kompilacja> konfiguracje> produkcja). W ten sposób możesz łatwo określić baseUrl dla każdego projektu.

Sprawdź, czy ustawienia są prawidłowe, przeglądając plik index.html zbudowanej aplikacji.

Katlu
źródło
7
wymaga to innej kompilacji dla każdego środowiska ze wszystkimi tego konsekwencjami.
Stavm
3

Dodatki: Jeśli chcesz na przykład: / users jako podstawę aplikacji dla routera i / public jako podstawę do wykorzystania zasobów

$ ng build -prod --base-href /users --deploy-url /public
3rdi
źródło
1

Miałem podobny problem, w rzeczywistości skończyło się na sprawdzeniu istnienia jakiegoś pliku w mojej sieci.

probePath byłby względnym adresem URL do pliku, który chcesz sprawdzić (rodzaj znacznika, jeśli jesteś teraz we właściwej lokalizacji), np. „asset / 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>
Ronald Korze
źródło
0

W pliku package.json ustaw flagę --base-href na ścieżkę względną:

"script": {
    "build": "ng build --base-href ./"
}
Jarek Wichrowski
źródło
-1

Szczerze mówiąc, twoja sprawa działa dla mnie dobrze!

Używam 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>
Aviw
źródło
Czy to rozwiązanie zadziałało dla Ciebie? Stoję przed problemem. Czy możesz również odpowiedzieć na mój post. stackoverflow.com/questions/53757931/…
Karan
1
Unikaj używania document.write (): developers.google.com/web/updates/2016/08/…
Patrick Hillert
-6

Właśnie zmieniłem:

  <base href="/">

do tego:

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

nie zapomnij zakończyć na /

Huy - Logarit
źródło