Angular 2/4/5 - Definir href da base dinamicamente

130

Temos um aplicativo corporativo que usa o Angular 2 para o cliente. Cada um de nossos clientes possui seu próprio URL exclusivo, ex: https://our.app.com/customer-onee https://our.app.com/customer-two. Atualmente, somos capazes de definir o <base href...>uso dinâmico document.location. Assim, o usuário acessa https://our.app.com/customer-twoe <base href...>é configurado para /customer-two... perfeito!

O problema é que, por exemplo, o usuário está em https://our.app.com/customer-two/another-pagee atualiza a página ou tenta acessar diretamente esse URL, o <base href...>conjunto é definido /customer-two/another-pagee os recursos não podem ser encontrados.

Tentamos o seguinte sem sucesso:

<!doctype html>
<html>

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

...

Infelizmente, estamos sem idéias. Qualquer ajuda seria incrível.

sharpmachine
fonte
1
Você já tentou alguma coisa com APP_BASE_HREF ? Não tenho certeza de como ele seria implementado, mas parece ser algo que seria útil ...
Jarod Moser
É claro que depende definitivamente de qual versão do roteador você está usando também. Você está no roteador versão 3.0.0-alpha.x?
Jarod Moser
Estou usando "@ angular / router": "3.0.0-alpha.7"
sharpmachine
5
Angular 7 - novembro de 2018. esse ainda é um problema. as respostas atuais fornecem uma solução parcial. fazer coisas dentro do módulo angular é tarde demais. como o HTML do seu índice é veiculado, ele precisa saber de onde extrair os scripts. existem apenas duas maneiras como eu a vejo - usando asp \ php \ para servir index.html com conteúdo dinâmico basehref. ou usando o javascript nativo diretamente no arquivo index.html para alterar o conteúdo do DOM antes que ele fique angular. ambos são soluções ruins para um problema comum. triste.
8898 Stavm
1
Alguém pode responder a minha pergunta também. stackoverflow.com/questions/53757931/…
Karan

Respostas:

166

A resposta marcada aqui não resolveu meu problema, possivelmente versões angulares diferentes. Consegui alcançar o resultado desejado com o seguinte angular clicomando no terminal / shell:

ng build --base-href /myUrl/

ng build --bh /myUrl/ ou ng build --prod --bh /myUrl/

Isso muda apenas <base href="https://stackoverflow.com/">para <base href="https://stackoverflow.com/myUrl/">a versão criada, o que foi perfeito para nossa mudança no ambiente entre desenvolvimento e produção. A melhor parte foi que nenhuma base de código requer alteração usando esse método.


Para resumir, deixe seu index.htmlhref base como: <base href="https://stackoverflow.com/">então execute ng build --bh ./com angular cli para torná-lo um caminho relativo ou substitua-o ./pelo que você precisar.


Atualizar:

Como o exemplo acima mostra como fazer isso na linha de comando, veja como adicioná-lo ao seu arquivo de configuração angular.json.

Isso será usado para todos ng servingno desenvolvimento local

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

Esta é a configuração específica para compilações de configurações, como prod:

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

A documentação oficial angular-cli referente ao uso.

Zze
fonte
35
Essa solução requer uma construção por cliente, o que provavelmente não está resolvendo o problema do OP.
Maxime Morin
1
@MaximeMorin, na minha experiência até agora, ./funciona para todos os locais. Então, na verdade, acho que você não precisa criar por cliente.
ZZE
9
@Zze Minha experiência com ./foi muito diferente. Ele funciona até o usuário navegar para uma rota e pressionar o botão ou as teclas de atualização do navegador. Nesse ponto, nossas regravações lidam com a chamada, veiculam a página de índice, mas o navegador assume que essa ./é a rota atual e não a pasta raiz do aplicativo Web. Resolvemos o problema do OP com um índice dinâmico (.aspx, .php, .jsp, etc) que define o href base.
Maxime Morin
2
O uso --proddurante a compilação não altera seu base hrefvalor, você não deve falar sobre isso aqui. --proddiz angular-clipara usar o environment.prod.tsarquivo em vez do arquivo padrão environment.tsna sua environmentspasta
Mattew Eon
1
@MattewEon Estava apenas demonstrando que era compatível com o --prodsinalizador, pois eles estavam falando sobre a implantação no OP.
Zze 11/0118
57

Aqui está o que acabamos fazendo.

Adicione isso ao index.html. Deve ser a primeira coisa na <head>seção

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

Em seguida, no app.module.tsarquivo, adicione { provide: APP_BASE_HREF, useValue: window['_app_base'] || '/' }à lista de provedores, assim:

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
fonte
Erro TS7017: O elemento implicitamente possui um tipo 'qualquer' porque o tipo 'Janela' não possui assinatura de índice. useValue: (window as any) ['_app_base'] || '/'ajuda
Erkan Buelbuel
Realmente não funciona para o ng2 mais recente, existe uma solução? (cargas mau caminho roteiros, em seguida, bom caminho roteiros, mas não no celular)
Amit
1
Adicionando-o como resposta abaixo, pois o tamanho do conteúdo é muito longo para um comentário.
Krishnan
1
@Mit Por favor, veja minha resposta abaixo para uma solução fácil no mais novo ng2.
Zze 7/02
3
Como você resolveu o problema com outros arquivos. Meu navegador está tentando carregar localhost: 8080 / main.bundle.js , mas não pode porque meu contextPath é diferente? (Deve ser atraente localhost: 8080 / olá / main.bundle.js. )
Sasa
33

Com base na sua resposta (@sharpmachine), fui capaz de refatorá-la um pouco mais, para que não precisemos hackear uma função 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.tscontém:

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
fonte
1
Tenho o myapp implantado na subpasta. Meu URL agora está assim: http://localhost:8080/subfolder/myapp/#/subfolder/myrouteé assim que o seu se parece? Você sabe como se livrar da subpasta após o # para que possa ser http://localhost:8080/subfolder/myapp/#/myroute?
precisa saber é o seguinte
Oh Acho que a base href é necessária apenas para o LocationStrategy. No meu caso, estou usando o HashLocationStrategy. Então, eu nem preciso configurá-lo. Você escreveu um código especial para lidar com a atualização com o LocationStrategy?
techguy2000
1
Sim, base hrefé necessário apenas para o LocationStrategyAFAIK. Quanto à gravação de código especial para lidar com a atualização com o LocationStrategy, não tenho certeza do que você quer dizer exatamente. Você está perguntando, e se meu "href base" for alterado durante uma atividade no meu aplicativo? Então, sim, eu tive que fazer uma coisa suja, como window.location.href = '/' + newBaseHref;onde era necessário mudar. Não tenho muito orgulho disso. Além disso, para dar uma atualização, eu me afastei dessas soluções de hackers no meu aplicativo, pois estava se tornando um problema de design para mim. Os parâmetros do roteador funcionam perfeitamente bem, por isso fiquei atento.
22417 Krishnan
1
Como é sua appRoutingProvideraparência, Krishnan? Vejo que a resposta aceita é a mesma; talvez você tenha copiado o dele providers, mas, se não, pode me dar uma amostra ou descrever seu propósito? A documentação apenas importsmódulos de roteamento , ele não usa quaisquer provedores de roteamento relacionadas (tanto quanto eu posso ver)
The Red Pea
@Krishnan Aqui está o que eu tenho a ver com nginx para fazer o trabalho:location /subfolder/myapp/ { try_files $uri /subfolder/myapp/index.html; } location /subfolder2/myapp/ { try_files $uri /subfolder2/myapp/index.html; }
techguy2000
24

Eu uso o diretório de trabalho atual ./ao criar vários aplicativos no mesmo domínio:

<base href="./">

Em uma nota lateral, eu uso .htaccesspara ajudar no meu roteamento na página recarregada:

RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule .* index.html [L]
Daerik
fonte
2
Muito obrigado pelo .htaccessarquivo
Shahriar Siraj Snigdho 27/03
8
Esta resposta está incorreta, não funciona para rotas como / your-app / a / b / c. Se você atualizar sua página a partir dessa rota, o Angular procurará o tempo de execução, polyfills e os principais arquivos JS e estilos de arquivo CSS em / your-app / a / b /. Eles não são, obviamente, naquele local, mas sob / your-app /
toongeorges
2
@toongeorges Esta resposta foi escrita há quase dois anos, usando um arquivo de configuração que é detectado e executado pelo software Apache Web Server para lidar com o roteamento no recarregamento da página. Para você, dizer que esta solução está incorreta e não funciona, é enganosa, mas o que você quer dizer é que você não conseguiu descobrir como fazê-la funcionar com sua configuração.
Daerik
Eu ignorei a regra de reescrita do Apache e não sei o suficiente para dizer que isso não funcionaria no Apache; portanto, posso estar errado e sua resposta pode estar correta. Embora, em qualquer caso, prefira uma solução que não exija a modificação da configuração do servidor, pois foram dadas várias respostas aqui, porque isso torna o aplicativo portátil em diferentes servidores.
toongeorges
2
Nah, @toongeorges está certo, mudar para <base href="./">está errado e vai estragar tudo com rotas como de /app/a/b/cfato, apenas testei. Prefira definir você mesmo o href base se estiver lidando apenas com um aplicativo da Web ou dê uma olhada na maneira angular de alterar o href base na implantação (existem muitas respostas aqui mencionando isso).
Giovannipds
15

Simplificação da resposta existente por @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 { }

Você não precisa especificar uma tag base em seu index.html, se estiver fornecendo valor para o token opaco APP_BASE_HREF.

Karanvir Kang
fonte
10

Este trabalho é bom para mim no ambiente prod

<base href="https://stackoverflow.com/" id="baseHref">
<script>
  (function() {
    document.getElementById('baseHref').href = '/' + window.location.pathname.split('/')[1] + "/";
  })();
</script>
Avikar
fonte
2
Esta solução tem uma grande limitação com várias configurações de VD no IIS. Isso causa o carregamento duplicado de pedaços.
Karan
9

No angular4, você também pode configurar o baseHref nos aplicativos .angular-cli.json.

em .angular-cli.json

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

isso atualizará o href base em index.html para MY_APP_BASE_HREF_1

ng build --app myapp1
Vince
fonte
4
Isso também precisa criar uma compilação para cada aplicativo.
Patrick Hillert 19/08/19
6

Se você estiver usando o Angular CLI 6, poderá usar as opções deployUrl / baseHref em angular.json (projetos> xxx> arquiteto> construção> configurações> configurações> produção). Dessa forma, você pode especificar facilmente o baseUrl por projeto.

Verifique se suas configurações estão corretas, consultando o index.html do aplicativo criado.

Katlu
fonte
7
isso requer uma construção diferente para cada ambiente, com todas as suas implicações.
Stavm
3

Complementos: se você quiser, por exemplo: / users como base de aplicativos para roteador e / public como base para uso de ativos

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

Tive um problema semelhante, na verdade, acabei investigando a existência de algum arquivo na minha web.

probePath seria o URL relativo ao arquivo que você deseja procurar (tipo de marcador se você estiver no local correto), por exemplo, '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
fonte
-1

Francamente falando, seu caso funciona bem para mim!

Estou usando o 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
fonte
Esta solução funcionou para você? Estou enfrentando o problema. Você também pode responder ao meu post. stackoverflow.com/questions/53757931/…
Karan
1
Evite usar document.write (): developers.google.com/web/updates/2016/08/…
Patrick Hillert 19/08/19
-6

Acabei de mudar:

  <base href="/">

para isso:

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

não esqueça de terminar com /

Huy - Logarit
fonte