Angolare 2/4/5 - Imposta dinamicamente href di base


131

Abbiamo un'app aziendale che utilizza Angular 2 per il client. Ciascuno dei nostri clienti ha il proprio URL univoco, ad esempio: https://our.app.com/customer-onee https://our.app.com/customer-two. Attualmente siamo in grado di impostare <base href...>dinamicamente utilizzando document.location. Quindi l'utente colpisce https://our.app.com/customer-twoe <base href...>viene impostato su /customer-two... perfetto!

Il problema è che se l'utente, ad esempio, si trova su https://our.app.com/customer-two/another-pagee aggiorna la pagina o tenta di colpire direttamente quell'URL, <base href...>viene impostato su /customer-two/another-pagee le risorse non possono essere trovate.

Abbiamo provato quanto segue senza alcun risultato:

<!doctype html>
<html>

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

...

Purtroppo siamo freschi di idee. Qualsiasi aiuto sarebbe fantastico.


1
Hai provato qualcosa con APP_BASE_HREF ? Non sono abbastanza sicuro di come sarebbe stato implementato, ma sembra essere qualcosa che sarebbe utile ...
Jarod Moser

Ovviamente dipende anche dalla versione del router che stai utilizzando. Utilizzi la versione del router 3.0.0-alpha.x?
Jarod Moser

Sto utilizzando "@ angular / router": "3.0.0-alpha.7"
sharpmachine

5
7 Angular - novembre 2018. questo è ancora un problema. le risposte attuali forniscono una soluzione parziale. fare cose all'interno del modulo angolare è troppo tardi. poiché il tuo indice HTML è servito, deve sapere da dove estrarre gli script. ci sono solo 2 modi per come la vedo io: usare asp \ php \ qualunque per servire index.html con contenuto dinamico di basehref. o utilizzando javascript nativo direttamente sul file index.html per modificare il contenuto DOM prima che diventi angolare. entrambe sono pessime soluzioni a un problema comune. triste.
Stavm

1
Qualcuno può rispondere anche alla mia domanda. stackoverflow.com/questions/53757931/…
Karan

Risposte:


166

La risposta contrassegnata qui non ha risolto il mio problema, forse diverse versioni angolari. Sono stato in grado di ottenere il risultato desiderato con il seguente angular clicomando in terminal / shell:

ng build --base-href /myUrl/

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

Questo cambia il <base href="https://stackoverflow.com/">per <base href="https://stackoverflow.com/myUrl/">nella versione generata solo che era perfetto per il nostro cambiamento di ambiente tra sviluppo e produzione. La parte migliore era che nessuna base di codice richiedeva modifiche utilizzando questo metodo.


Per riassumere, lascia il tuo index.htmlhref di base come: <base href="https://stackoverflow.com/">quindi esegui ng build --bh ./con angular cli per renderlo un percorso relativo, o sostituisci il ./con quello che ti serve.


Aggiornare:

Poiché l'esempio sopra mostra come farlo dalla riga di comando, ecco come aggiungerlo al tuo file di configurazione angular.json.

Questo sarà utilizzato per tutti ng servingper lo sviluppo locale

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

Questa è la configurazione specifica per build di configurazione come prod:

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

La documentazione ufficiale angular-cli relativa all'utilizzo.


35
Questa soluzione richiede una build per cliente, che probabilmente non risolve il problema dell'OP.
Maxime Morin

1
@ MaximeMorin, nella mia esperienza fino ad ora, ./funziona per tutte le località. Quindi in realtà non penso che tu debba costruire per cliente.
Zze

9
@Zze La mia esperienza con ./è stata molto diversa. Funziona finché l'utente non naviga su un percorso e preme il pulsante oi tasti di aggiornamento del browser. A quel punto, le nostre riscritture gestiscono la chiamata, servono la pagina dell'indice, ma il browser presume che ./sia il percorso corrente e non la cartella principale dell'applicazione web. Abbiamo risolto il problema dell'OP con un indice dinamico (.aspx, .php, .jsp, ecc.) Che imposta l'href di base.
Maxime Morin

2
L'utilizzo --proddurante la compilazione non cambierà il tuo base hrefvalore, non dovresti parlarne qui. --proddice angular-clidi usare il environment.prod.tsfile al posto del file predefinito environment.tsnella tua environmentscartella
Mattew Eon

1
@MattewEon Stava solo dimostrando che era compatibile con la --prodbandiera poiché si parlava di distribuzione nell'OP.
Zze

57

Ecco cosa abbiamo finito per fare.

Aggiungilo a index.html. Dovrebbe essere la prima cosa nella <head>sezione

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

Quindi nel app.module.tsfile, aggiungi { provide: APP_BASE_HREF, useValue: window['_app_base'] || '/' }all'elenco dei provider, in questo modo:

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 { }

Errore TS7017: l'elemento ha implicitamente un tipo "any" perché il tipo "Window" non ha firma di indice. useValue: (window as any) ['_app_base'] || '/'aiuta
Erkan Buelbuel

Non funziona davvero per l'ultimo ng2, c'è una soluzione? (carica il percorso degli script errati, quindi il percorso degli script buoni, ma non sui dispositivi mobili)
Amit

1
Aggiungendolo come risposta di seguito, poiché la lunghezza del contenuto è troppo lunga per un commento.
Krishnan

1
@Amit Si prega di vedere la mia risposta di seguito per una facile soluzione nel più recente ng2.
Zze

3
Come hai risolto il problema con altri file. Il mio browser sta tentando di caricare localhost: 8080 / main.bundle.js , ma non può perché il mio contextPath è diverso? (Dovrebbe essere recuperato localhost: 8080 / hello / main.bundle.js. )
Sasa

33

Sulla base della tua risposta (@sharpmachine), sono stato in grado di rifattorizzarlo ulteriormente un po ', in modo da non dover eliminare una funzione in 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 { }

E, ./shared/common-functions.util.tscontiene:

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
Ho myapp distribuito nella sottocartella. Il mio URL ora ha questo aspetto: http://localhost:8080/subfolder/myapp/#/subfolder/myrouteè questo il tuo aspetto? Sai come sbarazzarti della sottocartella dopo il # in modo che possa essere solo http://localhost:8080/subfolder/myapp/#/myroute?
techguy2000

Oh. Penso che la base href sia richiesta solo per LocationStrategy. Nel mio caso, sto usando HashLocationStrategy. Quindi non ho nemmeno bisogno di impostarlo. Hai scritto un codice speciale per gestire l'aggiornamento con LocationStrategy?
techguy2000

1
Sì, base hrefè richiesto solo per LocationStrategyAFAIK. Per quanto riguarda la scrittura di codice speciale per gestire l'aggiornamento con LocationStrategy, non sono sicuro di cosa intendi esattamente. Stai chiedendo, cosa succede se il mio "href di base" cambia durante un'attività nella mia app? Quindi, sì, ho dovuto fare una cosa sporca come window.location.href = '/' + newBaseHref;dove doveva essere cambiata. Non ne sono troppo orgoglioso. Inoltre, per fornire un aggiornamento, mi sono allontanato da queste soluzioni di hacking nella mia app poiché stava diventando un problema di progettazione per me. I parametri del router funzionano perfettamente, quindi ci sono rimasto fedele.
Krishnan

1
Che appRoutingProvideraspetto hai , Krishnan? Vedo che la risposta accettata è stata utilizzata allo stesso modo; forse hai solo copiato il suo providers, ma se no, puoi darmi un campione o descriverne lo scopo? La documentazione riguarda solo i importsmoduli di routing , non utilizza provider relativi al routing (per quanto posso vedere)
The Red Pea

@Krishnan Ecco cosa devo fare con nginx per farlo funzionare:location /subfolder/myapp/ { try_files $uri /subfolder/myapp/index.html; } location /subfolder2/myapp/ { try_files $uri /subfolder2/myapp/index.html; }
techguy2000

24

Uso la directory di lavoro corrente ./quando creo più app dallo stesso dominio:

<base href="./">

In una nota a margine, utilizzo .htaccessper assistere il mio routing al ricaricamento della pagina:

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

2
Grazie mille per il .htaccessfile
Shahriar Siraj Snigdho

8
Questa risposta non è corretta, non funziona per percorsi come / tua-app / a / b / c. Se aggiorni la tua pagina da un percorso di questo tipo, Angular cercherà il runtime, i polyfill e i file JS principali e il file CSS degli stili in / your-app / a / b /. Ovviamente non sono in quella posizione, ma sotto / your-app /
toongeorges

2
@toongeorges Questa risposta è stata scritta quasi due anni fa utilizzando un file di configurazione che viene rilevato ed eseguito dal software Apache Web Server per gestire il routing al ricaricamento della pagina. Per te dire che questa soluzione non è corretta e non funziona è fuorviante, ma ciò che intendi è che non sei riuscito a capire come farlo funzionare con la tua configurazione.
Daerik

Ho trascurato la regola di riscrittura di Apache e non ne so abbastanza per dire che non funzionerebbe su Apache, quindi potrei sbagliarmi e la tua risposta potrebbe essere corretta. Anche se in ogni caso preferisco una soluzione che non richieda di modificare la configurazione del server in quanto qui sono state fornite più risposte, perché questo rende l'applicazione portabile su server diversi.
toongeorges

2
No, @toongeorges ha ragione, cambiare in <base href="./">è sbagliato e rovinerà le rotte come in /app/a/b/ceffetti, l'ho appena testato. Preferisci impostare tu stesso l'href di base se hai a che fare solo con un'app Web o dai un'occhiata al modo angolare di cambiare l'href di base durante la distribuzione (ci sono molte risposte qui che lo menzionano).
giovannipds

15

Semplificazione della risposta esistente di @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 { }

Non è necessario specificare un tag di base nel tuo index.html, se stai fornendo un valore per il token opaco APP_BASE_HREF.


10

Questo funziona bene per me in ambiente prod

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

2
Questa soluzione ha una grande limitazione con l'installazione di più VD in IIS. Provoca il caricamento duplicato di blocchi.
Karan

9

In angular4, puoi anche configurare baseHref nelle app .angular-cli.json.

in .angular-cli.json

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

questo aggiornerà l'href di base in index.html a MY_APP_BASE_HREF_1

ng build --app myapp1

4
Questo deve anche creare una build per ogni app.
Patrick Hillert

6

Se stai usando Angular CLI 6, puoi usare le opzioni deployUrl / baseHref in angular.json (progetti> xxx> architetto> build> configurazioni> produzione). In questo modo, puoi facilmente specificare baseUrl per progetto.

Verifica che le tue impostazioni siano corrette guardando il index.html dell'app costruita.


7
ciò richiede una build diversa per ogni ambiente, con tutte le sue implicazioni.
Stavm

3

Add-on: se si desidera, ad esempio: / users come base dell'applicazione per il router e / public come base per l'utilizzo delle risorse

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

1

Ho avuto un problema simile, in realtà sono finito per sondare l'esistenza di alcuni file all'interno del mio web.

probePath sarebbe l'URL relativo al file che vuoi controllare (una specie di marker se ora ti trovi nella posizione corretta), ad esempio '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>

0

In package.json impostare flag --base-href sul percorso relativo:

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

-1

Francamente, il tuo caso funziona bene per me!

Sto usando 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>

Questa soluzione ha funzionato per te? Sto affrontando il problema. Puoi rispondere anche al mio post. stackoverflow.com/questions/53757931/…
Karan

1
Evita di utilizzare document.write (): developers.google.com/web/updates/2016/08/…
Patrick Hillert

-6

Ho appena cambiato:

  <base href="/">

a questa:

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

non dimenticare di terminare con /

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.