Winkel 2/4/5 - Basis href dynamisch einstellen

129

Wir haben eine Unternehmens-App, die Angular 2 für den Client verwendet. Jeder unserer Kunden hat seine eigene URL, z. B.: https://our.app.com/customer-oneUnd https://our.app.com/customer-two. Derzeit können wir das <base href...>dynamisch mit einstellen document.location. Der Benutzer trifft also https://our.app.com/customer-twound <base href...>wird eingestellt auf /customer-two... perfekt!

Das Problem ist, wenn der Benutzer zum Beispiel bei ist https://our.app.com/customer-two/another-pageund die Seite aktualisiert oder versucht, diese URL direkt zu treffen, die URL auf <base href...>gesetzt wird /customer-two/another-pageund die Ressourcen nicht gefunden werden können.

Wir haben Folgendes erfolglos versucht:

<!doctype html>
<html>

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

...

Leider haben wir keine Ideen mehr. Jede Hilfe wäre unglaublich.

sharpmachine
quelle
1
Haben Sie etwas mit APP_BASE_HREF versucht ? Ich bin nicht ganz sicher, wie es implementiert werden würde, aber es scheint etwas zu sein, das nützlich wäre ...
Jarod Moser
Natürlich hängt es definitiv davon ab, welche Version des Routers Sie gerade verwenden. Befinden Sie sich auf der Router-Version 3.0.0-alpha.x?
Jarod Moser
Ich benutze "@ angle / router": "3.0.0-alpha.7"
sharpmachine
5
Winkel 7 - November 2018. Dies ist immer noch ein Problem. Aktuelle Antworten bieten eine Teillösung. Dinge innerhalb des Winkelmoduls zu tun ist zu spät. Wenn Ihr Index-HTML bereitgestellt wird, muss er wissen, woher die Skripte stammen. Es gibt nur zwei Möglichkeiten, wie ich es sehe - asp \ php \ Whatever verwenden, um index.html mit dynamischem basehref-Inhalt bereitzustellen. oder verwenden Sie natives Javascript direkt in der Datei index.html, um den DOM-Inhalt zu ändern, bevor er eckig wird. beide sind schlechte Lösungen für ein häufiges Problem. traurig.
Stavm
1
Kann jemand bitte auch meine Anfrage beantworten. stackoverflow.com/questions/53757931/…
Karan

Antworten:

166

Die markierte Antwort hier hat mein Problem nicht gelöst, möglicherweise verschiedene Winkelversionen. Ich konnte das gewünschte Ergebnis mit dem folgenden angular cliBefehl in Terminal / Shell erzielen :

ng build --base-href /myUrl/

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

Dies ändert <base href="https://stackoverflow.com/">sich nur <base href="https://stackoverflow.com/myUrl/">in der erstellten Version, was perfekt für unseren Wechsel der Umgebung zwischen Entwicklung und Produktion war. Das Beste daran war, dass keine Codebasis mit dieser Methode geändert werden muss.


Um es zusammenzufassen, lassen Sie Ihre index.htmlBasis href wie folgt : <base href="https://stackoverflow.com/">Führen Sie sie dann ng build --bh ./mit eckigem cli aus, um sie zu einem relativen Pfad zu machen, oder ersetzen ./Sie sie durch das, was Sie benötigen.


Aktualisieren:

Da das obige Beispiel zeigt, wie es über die Befehlszeile ausgeführt wird, können Sie es hier zu Ihrer Konfigurationsdatei angle.json hinzufügen.

Dies wird für alle ng servingfür die lokale Entwicklung genutzt

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

Dies ist die Konfiguration, die für Konfigurationsbuilds wie prod spezifisch ist:

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

Die offizielle angular-cli Dokumentation zur Verwendung.

Zze
quelle
35
Diese Lösung erfordert einen Build pro Kunde, wodurch das Problem des OP wahrscheinlich nicht gelöst wird.
Maxime Morin
1
@MaximeMorin funktioniert nach meiner bisherigen Erfahrung ./für alle Standorte. Eigentlich glaube ich nicht, dass Sie pro Client bauen müssen.
Zze
9
@Zze Meine Erfahrung mit ./war sehr unterschiedlich. Dies funktioniert so lange, bis der Benutzer zu einer Route navigiert und die Aktualisierungstaste oder -tasten des Browsers drückt. Zu diesem Zeitpunkt bearbeiten unsere Umschreibungen den Anruf, dienen der Indexseite, aber der Browser geht davon aus, dass dies ./die aktuelle Route ist und nicht der Stammordner der Webanwendung. Wir haben das Problem des OP mit einem dynamischen Index (.aspx, .php, .jsp usw.) gelöst, der die Basis href festlegt.
Maxime Morin
2
Die Verwendung --prodwährend des Builds ändert nichts an Ihrem base hrefWert. Sie sollten hier nicht darüber sprechen. --prodweist angular-clian, die environment.prod.tsDatei anstelle der Standarddatei environment.tsin Ihrem environmentsOrdner zu verwenden
Mattew Eon
1
@MattewEon Es wurde nur demonstriert, dass es mit dem --prodFlag kompatibel ist, da über die Bereitstellung im OP gesprochen wurde.
Zze
57

Folgendes haben wir letztendlich getan.

Fügen Sie dies zu index.html hinzu. Es sollte das erste sein, was in diesem <head>Abschnitt steht

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

Fügen Sie dann in der app.module.tsDatei { provide: APP_BASE_HREF, useValue: window['_app_base'] || '/' }der Liste der Anbieter Folgendes hinzu:

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
quelle
Fehler TS7017: Das Element hat implizit den Typ 'any', da der Typ 'Window' keine Indexsignatur hat. useValue: (window as any) ['_app_base'] || '/'hilft
Erkan Buelbuel
Funktioniert nicht wirklich für das neueste ng2, gibt es eine Lösung? (Lädt einen Pfad für schlechte Skripte, dann einen Pfad für gute Skripte, aber nicht auf Mobilgeräten)
Amit
1
Fügen Sie es unten als Antwort hinzu, da die Länge des Inhalts für einen Kommentar zu lang ist.
Krishnan
1
@Amit Bitte sehen Sie meine Antwort unten für eine einfache Lösung in der neuesten ng2.
Zze
3
Wie haben Sie das Problem mit anderen Dateien gelöst? Mein Browser versucht, localhost: 8080 / main.bundle.js zu laden , kann dies jedoch nicht, da mein contextPath anders ist. (Es sollte localhost holen : 8080 / hello / main.bundle.js. )
Sasa
33

Aufgrund Ihrer Antwort (@sharpmachine) konnte ich sie ein wenig weiter überarbeiten, damit wir keine Funktion in hacken müssen 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 { }

Und ./shared/common-functions.util.tsenthält:

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
quelle
1
Ich habe myapp im Unterordner bereitgestellt. Meine URL sieht jetzt so aus: Sieht http://localhost:8080/subfolder/myapp/#/subfolder/myroutedeine so aus? Wissen Sie, wie Sie den Unterordner nach dem # entfernen können, damit er einfach sein kann http://localhost:8080/subfolder/myapp/#/myroute?
Techguy2000
Oh. Ich denke, dass href base nur für LocationStrategy erforderlich ist. In meinem Fall verwende ich HashLocationStrategy. Ich muss es also nicht einmal einstellen. Hatten Sie speziellen Code geschrieben, um die Aktualisierung mit LocationStrategy durchzuführen?
Techguy2000
1
Ja, base hrefist nur für LocationStrategyAFAIK erforderlich . Wenn Sie speziellen Code für die Aktualisierung mit LocationStrategy schreiben möchten, wissen Sie nicht genau, was Sie genau meinen. Fragen Sie sich, was passiert, wenn sich meine "Basis-HREF" während einer Aktivität in meiner App ändert? Dann musste ich ja eine schmutzige Sache machen, wie dort, window.location.href = '/' + newBaseHref;wo sie geändert werden musste. Ich bin nicht zu stolz darauf. Um ein Update zu geben, habe ich mich in meiner App von diesen Hack-Lösungen entfernt, da dies für mich zu einem Designproblem wurde. Router-Parameter funktionieren einwandfrei, also habe ich mich daran gehalten.
Krishnan
1
Wie appRoutingProvidersiehst du aus, Krishnan? Ich sehe, dass die akzeptierte Antwort dieselbe verwendet; Vielleicht haben Sie gerade seine kopiert providers, aber wenn nicht, können Sie mir eine Probe geben oder ihren Zweck beschreiben? Die Dokumentation nur importsRouting-Module , es werden keine Routing-bezogenen Anbieter verwendet (soweit ich sehen kann)
The Red Pea
@Krishnan Hier ist, was ich mit Nginx zu tun habe, damit es funktioniert:location /subfolder/myapp/ { try_files $uri /subfolder/myapp/index.html; } location /subfolder2/myapp/ { try_files $uri /subfolder2/myapp/index.html; }
techguy2000
24

Ich verwende das aktuelle Arbeitsverzeichnis, ./wenn ich mehrere Apps aus derselben Domain erstelle:

<base href="./">

Nebenbei bemerkt, ich .htaccessunterstütze beim Routing beim erneuten Laden von Seiten:

RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule .* index.html [L]
Daerik
quelle
2
Vielen Dank für die .htaccessDatei
Shahriar Siraj Snigdho
8
Diese Antwort ist falsch und funktioniert nicht für Routen wie / your-app / a / b / c. Wenn Sie Ihre Seite von einer solchen Route aus aktualisieren, sucht Angular unter / your-app / a / b / nach der Laufzeit-, Polyfill- und Haupt-JS-Datei und -Stil-CSS-Datei. Sie sind offensichtlich nicht an diesem Ort, sondern unter / your-app /
toongeorges
2
@toongeorges Diese Antwort wurde vor fast zwei Jahren mit einer Konfigurationsdatei geschrieben, die von der Apache Web Server-Software erkannt und ausgeführt wird, um das Routing beim erneuten Laden von Seiten zu handhaben. Es ist irreführend zu sagen, dass diese Lösung falsch ist und nicht funktioniert. Sie meinen jedoch, dass Sie nicht herausfinden konnten, wie Sie sie mit Ihrer Konfiguration zum Laufen bringen können.
Daerik
Ich habe die Apache-Umschreiberegel übersehen und weiß nicht genug darüber, um zu sagen, dass dies bei Apache nicht funktionieren würde. Daher kann ich mich irren und Ihre Antwort kann richtig sein. Auf jeden Fall bevorzuge ich eine Lösung, bei der Sie die Serverkonfiguration nicht ändern müssen, da hier mehrere Antworten gegeben wurden, da die Anwendung dadurch auf verschiedene Server portierbar ist.
Toongeorges
2
Nein, @toongeorges ist richtig, der Wechsel zu <base href="./">ist falsch und wird Routen wie in der /app/a/b/cTat vermasseln , habe es gerade getestet. Stellen Sie die Basis-HREF lieber selbst ein, wenn Sie sich nur mit einer Web-App befassen, oder werfen Sie einen Blick auf die eckige Art und Weise, die Basis-HREF bei der Bereitstellung zu ändern (hier gibt es viele Antworten, die dies erwähnen).
Giovannipds
15

Vereinfachung der bestehenden Antwort durch @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 { }

Sie müssen kein Basis-Tag in Ihrer index.html angeben, wenn Sie einen Wert für das undurchsichtige APP_BASE_HREF-Token angeben.

Karanvir Kang
quelle
10

Dies funktioniert gut für mich in Prod-Umgebung

<base href="https://stackoverflow.com/" id="baseHref">
<script>
  (function() {
    document.getElementById('baseHref').href = '/' + window.location.pathname.split('/')[1] + "/";
  })();
</script>
Avikar
quelle
2
Diese Lösung hat eine große Einschränkung bei der Einrichtung mehrerer VDs in IIS. Dies führt zum doppelten Laden von Chunks.
Karan
9

In angle4 können Sie baseHref auch in .angular-cli.json-Apps konfigurieren.

in .angular-cli.json

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

Dadurch wird die Basis href in index.html auf MY_APP_BASE_HREF_1 aktualisiert

ng build --app myapp1
Vince
quelle
4
Dies muss auch einen Build für jede App erstellen.
Patrick Hillert
6

Wenn Sie Angular CLI 6 verwenden, können Sie die Optionen deployUrl / baseHref in angular.json verwenden (Projekte> xxx> Architekt> ​​Build> Konfigurationen> Produktion). Auf diese Weise können Sie die baseUrl einfach pro Projekt angeben.

Überprüfen Sie, ob Ihre Einstellungen korrekt sind, indem Sie sich die index.html der erstellten App ansehen.

Katlu
quelle
7
Dies erfordert einen anderen Build für jede Umgebung mit all ihren Auswirkungen.
Stavm
3

Add-Ons: Wenn Sie beispielsweise: / Benutzer als Anwendungsbasis für Router und / public als Basis für die Verwendung von Assets verwenden möchten

$ ng build -prod --base-href /users --deploy-url /public
3rdi
quelle
1

Hatte ein ähnliches Problem, tatsächlich habe ich die Existenz einer Datei in meinem Web untersucht.

probePath ist die relative URL zu der Datei, nach der Sie suchen möchten (eine Art Markierung, wenn Sie sich jetzt am richtigen Speicherort befinden), z. B. '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>
Ronald Korze
quelle
-1

Ehrlich gesagt funktioniert Ihr Fall gut für mich!

Ich benutze 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
quelle
Hat diese Lösung bei Ihnen funktioniert? Ich stehe vor dem Problem. Kannst du bitte auch auf meinen Beitrag antworten? stackoverflow.com/questions/53757931/…
Karan
1
Bitte vermeiden Sie die Verwendung von document.write (): developer.google.com/web/updates/2016/08/…
Patrick Hillert
-5

Ich habe mich gerade geändert:

  <base href="/">

dazu:

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

Vergiss nicht, mit / zu enden

Huy - Logarit
quelle