$ location / chuyển đổi giữa html5 và chế độ hashbang / viết lại liên kết


179

Tôi có ấn tượng rằng Angular sẽ viết lại các URL xuất hiện trong các thuộc tính của thẻ neo trong tempaltes, sao cho chúng hoạt động cho dù ở chế độ html5 hay chế độ hashbang. Các tài liệu hướng dẫn sử dụng dịch vụ vị trí dường như để nói rằng HTML Link Viết lại sẽ chăm sóc của tình hình hashbang. Do đó, tôi hy vọng rằng khi không ở chế độ HTML5, băm sẽ được chèn và trong chế độ HTML5, chúng sẽ không.

Tuy nhiên, dường như không có việc viết lại đang diễn ra. Ví dụ sau không cho phép tôi chỉ thay đổi chế độ. Tất cả các liên kết trong ứng dụng sẽ cần phải được viết lại bằng tay (hoặc xuất phát từ một biến khi chạy. Tôi có bắt buộc phải viết lại tất cả các URL tùy thuộc vào chế độ không?

Tôi không thấy bất kỳ việc viết lại url phía máy khách nào đang diễn ra trong Angular 1.0.6, 1.1.4 hoặc 1.1.3. Có vẻ như tất cả các giá trị href cần được thêm vào với chế độ # / cho hashbang và / cho chế độ html5.

Có một số cấu hình cần thiết để gây ra viết lại? Tôi đang đọc sai các tài liệu? Làm điều gì khác ngớ ngẩn?

Đây là một ví dụ nhỏ:

<head>
    <script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.1.3/angular.js"></script>
</head>

<body>
    <div ng-view></div>
    <script>
        angular.module('sample', [])
            .config(
        ['$routeProvider', '$locationProvider',
            function ($routeProvider, $locationProvider) {

                //commenting out this line (switching to hashbang mode) breaks the app
                //-- unless # is added to the templates
                $locationProvider.html5Mode(true);

                $routeProvider.when('/', {
                    template: 'this is home. go to <a href="https://stackoverflow.com/about"/>about</a>'
                });
                $routeProvider.when('/about', {
                    template: 'this is about. go to <a href="https://stackoverflow.com/"/>home</a'
                });
            }
        ])
            .run();
    </script>
</body>

Phụ lục: khi đọc lại câu hỏi của tôi, tôi thấy rằng tôi đã sử dụng thuật ngữ "viết lại" mà không có nhiều sự rõ ràng về việc ai và khi nào tôi muốn viết lại. Câu hỏi là về cách làm cho Angular viết lại các URL khi nó hiển thị các đường dẫn và làm thế nào để nó diễn giải các đường dẫn trong mã JS thống nhất qua hai chế độ. Đây không phải là về cách làm cho máy chủ web thực hiện việc viết lại các yêu cầu tương thích với HTML5.


1
Đây là giải pháp cho Angular 1.6 .
Mistalis

Câu trả lời:


361

Các tài liệu không rõ ràng về định tuyến AngularJS. Nó nói về chế độ Hashbang và HTML5. Trên thực tế, định tuyến AngularJS hoạt động ở ba chế độ:

  • Chế độ Hashbang
  • Chế độ HTML5
  • Hashbang trong Chế độ HTML5

Đối với mỗi chế độ, có một lớp LocationUrl tương ứng (LocationHashbangUrl, LocationUrl và LocationHashbangInHTML5Url).

Để mô phỏng việc viết lại URL, bạn thực sự phải đặt html5mode thành true và trang trí lớp $ sniffer như sau:

$provide.decorator('$sniffer', function($delegate) {
  $delegate.history = false;
  return $delegate;
});

Bây giờ tôi sẽ giải thích điều này chi tiết hơn:

Chế độ Hashbang

Cấu hình:

$routeProvider
  .when('/path', {
    templateUrl: 'path.html',
});
$locationProvider
  .html5Mode(false)
  .hashPrefix('!');

Đây là trường hợp khi bạn cần sử dụng URL có băm trong các tệp HTML của mình, chẳng hạn như trong

<a href="index.html#!/path">link</a>

Trong Trình duyệt, bạn phải sử dụng Liên kết sau: http://www.example.com/base/index.html#!/base/path

Như bạn có thể thấy trong chế độ Hashbang thuần túy, tất cả các liên kết trong tệp HTML phải bắt đầu bằng cơ sở, chẳng hạn như "index.html #!".

Chế độ HTML5

Cấu hình:

$routeProvider
  .when('/path', {
    templateUrl: 'path.html',
  });
$locationProvider
  .html5Mode(true);

Bạn nên đặt cơ sở trong tệp HTML

<html>
  <head>
    <base href="/">
  </head>
</html>

Trong chế độ này, bạn có thể sử dụng các liên kết mà không có # trong tệp HTML

<a href="/path">link</a>

Liên kết trong Trình duyệt:

http://www.example.com/base/path

Hashbang trong Chế độ HTML5

Chế độ này được kích hoạt khi chúng ta thực sự sử dụng chế độ HTML5 nhưng trong một trình duyệt không tương thích. Chúng ta có thể mô phỏng chế độ này trong một trình duyệt tương thích bằng cách trang trí dịch vụ $ sniffer và đặt lịch sử thành false.

Cấu hình:

$provide.decorator('$sniffer', function($delegate) {
  $delegate.history = false;
  return $delegate;
});
$routeProvider
  .when('/path', {
    templateUrl: 'path.html',
  });
$locationProvider
  .html5Mode(true)
  .hashPrefix('!');

Đặt cơ sở trong tệp HTML:

<html>
  <head>
    <base href="/">
  </head>
</html>

Trong trường hợp này, các liên kết cũng có thể được viết mà không cần băm trong tệp HTML

<a href="/path">link</a>

Liên kết trong Trình duyệt:

http://www.example.com/index.html#!/base/path

Cảm ơn bạn đã giải thích chi tiết, @jupiter. Tôi sẽ xem liệu tôi có thể bỏ qua tiếng nổ hay không và giữ băm và lừa Angular không yêu cầu hai bộ URL tùy thuộc vào chế độ!
laurelnaiad

1
Tôi nghĩ rằng tôi đã không hiểu đúng vấn đề của bạn. Tại sao bạn không sử dụng URL mà không cần băm? Chúng sẽ hoạt động trong các trình duyệt hỗ trợ API lịch sử và các trình duyệt không hỗ trợ API lịch sử. AngularJS sẽ đưa phiên bản # vào thanh vị trí khi bạn nhấp vào chúng trong các trình duyệt không hỗ trợ API lịch sử vì AngularJS chặn các nhấp chuột vào liên kết.
Sao Mộc

Tôi đang làm việc trên một khung nên hỗ trợ cả hai chế độ. Các tác giả ứng dụng có thể chọn cái này hoặc cái kia mà không cần lo lắng về việc có hay không có băm trong các mẫu của họ và / hoặc thay đổi trong việc giải thích các đường dẫn tương đối. Tôi hy vọng rằng mẹo của bạn sẽ giúp làm cho đúng rằng "tất cả những gì bạn làm là thay đổi chế độ" (ngay cả khi giải pháp thực tế là "đặt chế độ thành html5 và sau đó nói dối về khả năng của trình duyệt").
laurelnaiad

1
Tôi nhận được $providelà không xác định?
Petrus Theron

1
@pate - bạn cần thêm $ cung cấp chức năng cấu hình khi bạn thiết lập trình trang trí.
laurelnaiad

8

Các độc giả tương lai, nếu bạn đang sử dụng Angular 1.6 , bạn cũng cần thay đổi hashPrefix:

appModule.config(['$locationProvider', function($locationProvider) {
    $locationProvider.html5Mode(true);
    $locationProvider.hashPrefix('');
}]);

Đừng quên đặt cơ sở trong HTML của bạn <head>:

<head>
    <base href="/">
    ...
</head>

Thông tin thêm về các thay đổi here.


3
cảm ơn @Mistalis. câu trả lời của bạn hoạt động tốt. nhưng vấn đề khi tôi làm mới trang sau khi định tuyến, không tìm thấy lỗi trang. hãy giúp tôi ..
Ashish Mehta

@AshishMehta Xin chào Ashish. Tôi đề nghị bạn đọc câu trả lời này , nó có thể giải quyết vấn đề của bạn. Tạm biệt! :)
Mistalis

0

Điều này khiến tôi mất một thời gian để tìm ra vì vậy đây là cách tôi làm cho nó hoạt động - Định tuyến WebAPI Angular mà không có # cho SEO

  1. thêm vào Index.html - base href = "/">
  2. Thêm $ locationProvider.html5Mode (đúng); vào app.config

  3. Tôi cần một bộ điều khiển nhất định (có trong bộ điều khiển gia đình) bị bỏ qua để tải lên hình ảnh vì vậy tôi đã thêm quy tắc đó vào RouteConfig

         routes.MapRoute(
            name: "Default2",
            url: "Home/{*.}",
            defaults: new { controller = "Home", action = "SaveImage" }
        );
  4. Trong Global.asax, hãy thêm vào như sau - đảm bảo bỏ qua đường dẫn tải lên hình ảnh và api để chúng hoạt động như bình thường nếu không thì định tuyến lại mọi thứ khác.

     private const string ROOT_DOCUMENT = "/Index.html";
    
    protected void Application_BeginRequest(Object sender, EventArgs e)
    {
        var path = Request.Url.AbsolutePath;
        var isApi = path.StartsWith("/api", StringComparison.InvariantCultureIgnoreCase);
        var isImageUpload = path.StartsWith("/home", StringComparison.InvariantCultureIgnoreCase);
    
        if (isApi || isImageUpload)
            return;
    
        string url = Request.Url.LocalPath;
        if (!System.IO.File.Exists(Context.Server.MapPath(url)))
            Context.RewritePath(ROOT_DOCUMENT);
    }
  5. Đảm bảo sử dụng $ location.url ('/ XXX') chứ không phải window.location ... để chuyển hướng

  6. Tham chiếu các tệp CSS với đường dẫn tuyệt đối

và không

<link href="app/content/bootstrapwc.css" rel="stylesheet" />

Lưu ý cuối cùng - thực hiện theo cách này đã cho tôi toàn quyền kiểm soát và tôi không cần phải làm gì với cấu hình web.

Hy vọng điều này sẽ giúp vì điều này làm tôi mất một thời gian để tìm ra.


0

Tôi muốn có thể truy cập ứng dụng của mình bằng chế độ HTML5 và mã thông báo cố định và sau đó chuyển sang phương thức hashbang (để giữ mã thông báo để người dùng có thể làm mới trang của mình).

URL để truy cập ứng dụng của tôi:

http://myapp.com/amazing_url?token=super_token

Sau đó, khi người dùng tải trang:

http://myapp.com/amazing_url?token=super_token#/amazing_url

Sau đó, khi người dùng điều hướng:

http://myapp.com/amazing_url?token=super_token#/another_url

Với điều này, tôi giữ mã thông báo trong URL và giữ trạng thái khi người dùng đang duyệt. Tôi đã mất một chút khả năng hiển thị của URL, nhưng không có cách nào hoàn hảo để làm điều đó.

Vì vậy, không kích hoạt chế độ HTML5 và sau đó thêm bộ điều khiển này:

.config ($stateProvider)->
    $stateProvider.state('home-loading', {
         url: '/',
         controller: 'homeController'
    })
.controller 'homeController', ($state, $location)->
    if window.location.pathname != '/'
        $location.url(window.location.pathname+window.location.search).replace()
    else
        $state.go('home', {}, { location: 'replace' })
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.