Angular 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 หรือไม่
จรดโมเซอร์

ฉันใช้ "@ angular / router": "3.0.0-alpha.7"
sharpmachine

5
Angular 7 - พฤศจิกายน 2018 นี้ยังคงเป็นปัญหา คำตอบปัจจุบันให้คำตอบบางส่วน การทำสิ่งต่างๆภายในโมดูลเชิงมุมนั้นสายเกินไป เนื่องจากดัชนี HTML ของคุณแสดงผลจำเป็นต้องทราบว่าจะดึงสคริปต์มาจากที่ใด มีเพียง 2 วิธีเท่านั้นที่ฉันเห็นคือการใช้ asp \ php \ อะไรก็ได้เพื่อให้บริการ index.html ด้วยเนื้อหาแบบไดนามิก basehref หรือใช้จาวาสคริปต์ดั้งเดิมบนไฟล์ index.html เพื่อเปลี่ยนเนื้อหา DOM ก่อนที่จะเปลี่ยนเป็นเชิงมุม ทั้งสองวิธีแก้ปัญหาทั่วไปได้ไม่ดี เสียใจ
Stavm

1
ใครช่วยตอบคำถามของฉันด้วย stackoverflow.com/questions/53757931/…
Karan

คำตอบ:


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.htmlhref ฐานของคุณเป็น: <base href="https://stackoverflow.com/">จากนั้นเรียกใช้ng build --bh ./ด้วย angular cli เพื่อทำให้เป็นเส้นทางสัมพัทธ์หรือแทนที่./ด้วยสิ่งที่คุณต้องการ


ปรับปรุง:

ดังตัวอย่างด้านบนแสดงวิธีการดำเนินการจากบรรทัดคำสั่งต่อไปนี้เป็นวิธีเพิ่มลงในไฟล์กำหนดค่า 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
โซลูชันนี้ต้องการการสร้างต่อลูกค้าซึ่งอาจไม่สามารถแก้ปัญหาของ OP ได้
Maxime Morin

1
@MaximeMorin จากประสบการณ์ของฉันจนถึงตอนนี้การทำเช่นนี้./ใช้ได้กับทุกสถานที่ จริงๆแล้วฉันไม่คิดว่าคุณต้องสร้างต่อลูกค้า
Zze

9
@ Zze ประสบการณ์ของฉัน./แตกต่างกันมาก ใช้งานได้จนกว่าผู้ใช้จะไปยังเส้นทางและกดปุ่มรีเฟรชของเบราว์เซอร์หรือคีย์ เมื่อถึงจุดนั้นการเขียนซ้ำของเราจะจัดการกับการโทรให้บริการหน้าดัชนี แต่เบราว์เซอร์จะถือว่า./เป็นเส้นทางปัจจุบันไม่ใช่โฟลเดอร์รูทของเว็บแอป เราแก้ไขปัญหาของ OP ด้วยดัชนีไดนามิก (.aspx, .php, .jsp ฯลฯ ) ซึ่งตั้งค่า href พื้นฐาน
Maxime Morin

2
การใช้--prodระหว่างการสร้างจะไม่เปลี่ยนbase hrefค่าของคุณคุณไม่ควรพูดถึงที่นี่ --prodบอกangular-cliให้ใช้environment.prod.tsไฟล์แทนไฟล์เริ่มต้นenvironment.tsในenvironmentsโฟลเดอร์ของคุณ
Mattew Eon

1
@MattewEon มันแค่แสดงให้เห็นว่ามันเข้ากันได้กับ--prodแฟล็กเนื่องจากพวกเขากำลังพูดถึงการปรับใช้ใน OP
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'] || '/'ช่วย
Erkan Buelbuel

ใช้งานไม่ได้จริงสำหรับ ng2 ล่าสุดมีวิธีแก้ไขหรือไม่? (โหลดเส้นทางสคริปต์ที่ไม่ดีจากนั้นเส้นทางสคริปต์ที่ดี แต่ไม่ใช่บนมือถือ)
Amit

1
เพิ่มเป็นคำตอบด้านล่างเนื่องจากความยาวของเนื้อหายาวเกินไปสำหรับความคิดเห็น
Krishnan

1
@Amit โปรดดูคำตอบของฉันด้านล่างเพื่อการแก้ปัญหาที่ง่ายใน ng2 ใหม่ล่าสุด
Zze

3
คุณแก้ปัญหากับไฟล์อื่นได้อย่างไร เบราว์เซอร์ของฉันพยายามโหลดlocalhost: 8080 / main.bundle.jsแต่ทำไม่ได้เพราะ contextPath ของฉันแตกต่างกัน? (ควรเรียกlocalhost: 8080 / hello / main.bundle.js )
Sasa

33

จากคำตอบ (@sharpmachine) ของคุณฉันสามารถ refactor เพิ่มเติมได้เล็กน้อยเพื่อที่เราจะได้ไม่ต้องแฮ็คฟังก์ชันใน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
ฉันได้ติดตั้งแอปของฉันกับโฟลเดอร์ย่อยแล้ว ตอนนี้ 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;ที่ต้องเปลี่ยน ฉันไม่ภูมิใจกับมันมากเกินไป นอกจากนี้เพื่อให้การอัปเดตฉันได้ย้ายออกจากโซลูชันแฮ็คเหล่านี้ในแอปของฉันเนื่องจากมันกำลังกลายเป็นปัญหาในการออกแบบสำหรับฉัน พารามิเตอร์ของเราเตอร์ทำงานได้ดีดังนั้นฉันจึงติดอยู่กับมัน
Krishnan

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ไฟล์
Shahriar Siraj Snigdho

8
คำตอบนี้ไม่ถูกต้องใช้ไม่ได้กับเส้นทางเช่น / your-app / a / b / c หากคุณรีเฟรชหน้าของคุณจากเส้นทางดังกล่าว Angular จะค้นหาไฟล์รันไทม์โพลีฟิลล์และไฟล์ JS หลักและสไตล์ไฟล์ CSS ภายใต้ / your-app / a / b / เห็นได้ชัดว่าพวกเขาไม่ได้อยู่ที่ตำแหน่งนั้น แต่อยู่ภายใต้ / your-app /
toongeorges

2
@toongeorges คำตอบนี้เขียนขึ้นเมื่อเกือบสองปีที่แล้วโดยใช้ไฟล์คอนฟิกูเรชันที่ซอฟต์แวร์ Apache Web Server ตรวจพบและดำเนินการเพื่อจัดการการกำหนดเส้นทางในการโหลดหน้าซ้ำ เพื่อให้คุณบอกว่าโซลูชันนี้ไม่ถูกต้องและใช้งานไม่ได้ทำให้เข้าใจผิด แต่สิ่งที่คุณหมายถึงคือคุณไม่สามารถหาวิธีทำให้มันทำงานกับการกำหนดค่าของคุณได้
Daerik

ฉันมองข้ามกฎการเขียนซ้ำของ Apache และฉันไม่รู้มากพอที่จะบอกว่าสิ่งนี้จะใช้ไม่ได้กับ Apache ดังนั้นฉันอาจจะผิดและคำตอบของคุณอาจถูกต้อง แม้ว่าในกรณีใดก็ตามฉันชอบโซลูชันที่ไม่ต้องการให้คุณแก้ไขการกำหนดค่าเซิร์ฟเวอร์เนื่องจากมีคำตอบหลายข้อที่นี่เนื่องจากทำให้แอปพลิเคชันพกพาได้ในเซิร์ฟเวอร์ต่างๆ
toongeorges

2
Nah @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

งานนี้ดีสำหรับฉันในสภาพแวดล้อมที่แยง

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

2
โซลูชันนี้มีข้อ จำกัด อย่างมากกับการตั้งค่า VD หลายรายการใน IIS ทำให้เกิดการโหลดซ้ำของชิ้น
Karan

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
นอกจากนี้ยังต้องสร้างรุ่นสำหรับแต่ละแอป
Patrick Hillert

6

หากคุณใช้ Angular CLI 6 คุณสามารถใช้อ็อพชัน deployUrl / baseHref ใน angular.json (projects> xxx> architect> build> configuration> production) ด้วยวิธีนี้คุณสามารถระบุ baseUrl ต่อโปรเจ็กต์ได้อย่างง่ายดาย

ตรวจสอบว่าการตั้งค่าของคุณถูกต้องโดยดูที่ index.html ของแอพที่สร้างขึ้น


7
สิ่งนี้ต้องการการสร้างที่แตกต่างกันสำหรับแต่ละสภาพแวดล้อมโดยมีผลกระทบทั้งหมด
Stavm

3

Add-ons: หากคุณต้องการเช่น: / users as application base for router and / public as base for assets use

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

1

มีปัญหาที่คล้ายกันจริงๆแล้วฉันลงเอยด้วยการตรวจสอบการมีอยู่ของไฟล์บางไฟล์ในเว็บของฉัน

probePathจะเป็น URL สัมพัทธ์ของไฟล์ที่คุณต้องการตรวจสอบ (ชนิดของเครื่องหมายหากคุณอยู่ในตำแหน่งที่ถูกต้อง) เช่น '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>


-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/…
Karan

1
โปรดหลีกเลี่ยงการใช้ document.write (): developers.google.com/web/updates/2016/08/…
Patrick Hillert

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.