$ розташування / перемикання між html5 та режимом хешбанг / перезапис посилань


179

У мене склалося враження, що Angular перепише URL-адреси, які відображаються в атрибутах href анкерних тегів всередині tempaltes, таким чином, щоб вони працювали в режимі html5 або в режимі хешбангу. Документація на послуги визначення місця розташування , здається, говорять , що HTML Link Rewriting піклується про ситуацію hashbang. Таким чином, я б очікував, що, коли не в режимі HTML5, хеші будуть вставлені, а в режимі HTML5 - не.

Однак, схоже, ніякого переписування не відбувається. Наступний приклад не дозволяє мені просто змінити режим. Усі посилання в додатку потрібно буде переписати вручну (або отримати від змінної під час виконання. Чи потрібно мені вручну переписати всі URL-адреси залежно від режиму?

Я не бачу жодного перезапису URL-адреси на стороні клієнта у Angular 1.0.6, 1.1.4 або 1.1.3. Здається, що всі значення href потрібно попередньо встановити в режимі # / для хешбангу та / для режиму html5.

Чи потрібна якась конфігурація, щоб викликати перезапис? Я неправильно читаю документи? Робити щось інше нерозумно?

Ось невеликий приклад:

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

Додавання: перечитуючи моє запитання, я бачу, що я використовував термін «переписування», не маючи ясності щодо того, хто і коли хотів зробити переписування. Питання полягає в тому, як змусити Angular переписати URL-адреси, коли він відображає шляхи, і як змусити його інтерпретувати шляхи в коді JS рівномірно в двох режимах. Справа не в тому, як змусити веб-сервер робити перезаписи запитів, сумісних з HTML5.


1
Ось рішення для Angular 1.6 .
Mistalis

Відповіді:


361

Документація не дуже чітка щодо маршрутизації AngularJS. У ньому йдеться про режим Hashbang та HTML5. Насправді маршрутизація AngularJS працює в трьох режимах:

  • Режим Хашбанг
  • Режим HTML5
  • Hashbang в режимі HTML5

Для кожного режиму є відповідний клас LocationUrl (LocationHashbangUrl, LocationUrl та LocationHashbangInHTML5Url).

Щоб імітувати перезапис URL-адрес, потрібно фактично встановити html5mode на істинне та прикрасити клас $ sniffer наступним чином:

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

Зараз я поясню це більш детально:

Режим Хашбанг

Конфігурація:

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

Це той випадок, коли вам потрібно використовувати URL-адреси з хешами у своїх HTML-файлах, наприклад, у

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

У браузері потрібно використовувати таке посилання: http://www.example.com/base/index.html#!/base/path

Як ви бачите в чистому режимі Hashbang, всі посилання у файлах HTML повинні починатися з бази, наприклад "index.html #!".

Режим HTML5

Конфігурація:

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

Вам слід встановити базу в HTML-файлі

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

У цьому режимі ви можете використовувати посилання без # у файлах HTML

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

Посилання в браузері:

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

Hashbang в режимі HTML5

Цей режим активується, коли ми фактично використовуємо режим HTML5, але в несумісному браузері. Ми можемо імітувати цей режим у сумісному браузері, прикрасивши послугу $ sniffer та встановивши історію на false.

Конфігурація:

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

Встановити базу в HTML-файлі:

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

У цьому випадку посилання також можна записати без хешу у файл HTML

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

Посилання в браузері:

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

Дякую за детальне пояснення, @jupiter. Я перегляну, чи можу я пропустити вибух і зберегти хеш і хитрість Angular не вимагати двох наборів URL-адрес залежно від режиму!
laurelnaiad

1
Я думаю, що я неправильно зрозумів вашу проблему. Чому ви не використовуєте лише URL-адреси без хешу? Вони працюватимуть у веб-переглядачах, які підтримують API історії, і в браузерах, що не підтримують API історії. AngularJS помістить # версію в рядок розташування, коли ви клацнете на них у браузерах, що не підтримують API історії, оскільки AngularJS перехоплює кліки посилання.
юпітер

Я працюю над рамкою, яка повинна підтримувати обидва режими. Автори програм повинні мати можливість вибирати те чи інше, не турбуючись про те, чи є хеши в їх шаблонах та / або зміни в інтерпретації відносних шляхів. Я сподіваюся, що ваш трюк допоможе зрозуміти, що "все, що ви робите, це змінити режим" (навіть якщо практичним рішенням є "встановити режим на html5, а потім залежати від можливостей браузера").
laurelnaiad

1
Я отримую $provideне визначений?
Петрус Терон

1
@pate - під час налаштування декоратора потрібно вставити $ config у функцію налаштування.
laurelnaiad

8

Для майбутніх читачів, якщо ви використовуєте Angular 1.6 , вам також потрібно змінити hashPrefix:

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

Не забудьте встановити базу у своєму HTML <head>:

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

Більше інформації про журнал змін here.


3
дякую @Mistalis Ваша відповідь працює чудово. але проблема, коли я оновлюю сторінку після маршрутизації, виникають помилки на сторінці, які не знайдено. будь ласка, допоможіть мені ..
Ashish Mehta

@AshishMehta Привіт Ашиш. Я пропоную вам прочитати цю відповідь , це може вирішити вашу проблему. До побачення! :)
Mistalis

0

Це знадобило мені деякий час, щоб зрозуміти, так ось я змусив це працювати - Angular WebAPI ASP Routing без # для SEO

  1. додати до Index.html - base href = "/">
  2. Додати $ locationProvider.html5Мод (вірно); до app.config

  3. Мені потрібен був певний контролер (який був у домашньому контролері), щоб його ігнорували для завантаження зображень, тому я додав це правило до RouteConfig

         routes.MapRoute(
            name: "Default2",
            url: "Home/{*.}",
            defaults: new { controller = "Home", action = "SaveImage" }
        );
  4. У Global.asax додайте наступне - переконайтесь, що ігноруйте шляхи завантаження api та зображень, нехай вони функціонують як звичайні, інакше перепрограмуйте все інше.

     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. Обов’язково використовуйте $ location.url ('/ XXX'), а не window.location ... для переадресації

  6. Посилайтеся на файли CSS з абсолютним шляхом

і ні

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

Підсумкове зауваження - таким чином я дав мені повний контроль, і мені не потрібно було нічого робити з веб-конфігурацією.

Сподіваюся, що це допомагає, оскільки це знадобило мені час, щоб розібратися.


0

Я хотів мати доступ до своєї програми в режимі HTML5 та фіксованого маркера, а потім перейти на метод хешбангу (щоб зберегти маркер, щоб користувач міг оновити свою сторінку).

URL-адреса для доступу до мого додатка:

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

Потім, коли користувач завантажує сторінку:

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

Потім, коли користувач переходить:

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

Завдяки цьому я зберігаю маркер у URL-адресі та зберігаю стан, коли користувач переглядає. Я втратив трохи видимість URL-адреси, але немає ідеального способу зробити це.

Тому не вмикайте режим HTML5, а потім додайте цей контролер:

.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' })
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.