$ location / przełączanie między trybem HTML5 a hashbang / przepisywanie linków


179

Miałem wrażenie, że Angular przepisze adresy URL pojawiające się w atrybutach href tagów zakotwiczenia w temaltalach, tak aby działały zarówno w trybie HTML5, jak i Hashbang. Dokumentacja za usługę lokalizacji zdaje się mówić, że HTML Link Przepisywanie dba o sytuacji hashbang. Spodziewałbym się zatem, że gdy nie będzie w trybie HTML5, skróty zostaną wstawione, aw trybie HTML5 nie.

Wydaje się jednak, że przepisywanie nie ma miejsca. Poniższy przykład nie pozwala mi po prostu zmienić trybu. Wszystkie linki w aplikacji będą musiały zostać przepisane ręcznie (lub uzyskane ze zmiennej w czasie wykonywania. Czy muszę ręcznie przepisać wszystkie adresy URL w zależności od trybu?

Nie widzę żadnego przepisywania adresów URL po stronie klienta w Angular 1.0.6, 1.1.4 lub 1.1.3. Wydaje się, że wszystkie wartości href muszą być poprzedzone znakiem # / dla trybu hashbang i / dla trybu html5.

Czy konieczna jest konfiguracja, aby spowodować przepisywanie? Czy źle czytam dokumenty? Robisz coś głupiego?

Oto mały przykład:

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

Dodatek: czytając ponownie moje pytanie, widzę, że użyłem terminu „przepisywanie” bez dużej jasności co do tego, kto i kiedy chciałbym dokonać przepisania. Pytanie dotyczy tego, w jaki sposób zmusić program Angular do przepisania adresów URL podczas renderowania ścieżek oraz w jaki sposób zmusić go do interpretacji ścieżek w kodzie JS w sposób jednolity w obu trybach. To nie o tym, jak spowodować, że serwer WWW, aby zrobić HTML5 zgodny przepisywanie wniosków.


1
Oto rozwiązanie dla Angulara 1.6 .
Mistalis

Odpowiedzi:


361

Dokumentacja nie jest bardzo jasna na temat routingu AngularJS. Mówi o trybie Hashbang i HTML5. W rzeczywistości routing AngularJS działa w trzech trybach:

  • Tryb Hashbang
  • Tryb HTML5
  • Hashbang w trybie HTML5

Dla każdego trybu istnieje odpowiednia klasa LocationUrl (LocationHashbangUrl, LocationUrl i LocationHashbangInHTML5Url).

Aby zasymulować przepisywanie adresów URL, musisz ustawić wartość html5mode na true i udekorować klasę $ sniffer w następujący sposób:

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

Wyjaśnię to teraz bardziej szczegółowo:

Tryb Hashbang

Konfiguracja:

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

Dzieje się tak, gdy trzeba używać adresów URL z skrótami w plikach HTML, takich jak

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

W przeglądarce musisz użyć następującego linku: http://www.example.com/base/index.html#!/base/path

Jak widać w czystym trybie Hashbang, wszystkie linki w plikach HTML muszą zaczynać się od podstawy, np. „Index.html #!”.

Tryb HTML5

Konfiguracja:

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

Powinieneś ustawić bazę w pliku HTML

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

W tym trybie możesz używać linków bez # w plikach HTML

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

Link w przeglądarce:

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

Hashbang w trybie HTML5

Ten tryb jest aktywowany, gdy faktycznie używamy trybu HTML5, ale w niekompatybilnej przeglądarce. Możemy symulować ten tryb w kompatybilnej przeglądarce, dekorując usługę $ sniffer i ustawiając historię na false.

Konfiguracja:

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

Ustaw bazę w pliku HTML:

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

W takim przypadku łącza można również zapisać bez skrótu w pliku HTML

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

Link w przeglądarce:

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

Dziękuję za szczegółowe wyjaśnienie, @jupiter. Zobaczę, czy mogę pominąć huk i zachować skrót i oszukać Angular, aby nie wymagał dwóch zestawów adresów URL w zależności od trybu!
laurelnaiada

1
Myślę, że nie zrozumiałem właściwie twojego problemu. Dlaczego nie używasz tylko adresów URL bez skrótu? Będą działać w przeglądarkach obsługujących interfejs API historii i przeglądarkach nieobsługujących interfejsu API historii. AngularJS umieści wersję # w pasku lokalizacji, gdy klikniesz je w przeglądarkach nieobsługujących interfejsu API historii, ponieważ AngularJS przechwytuje kliknięcia linków.
Jowisz

Pracuję nad strukturą, która powinna obsługiwać oba tryby. Autorzy aplikacji powinni mieć możliwość wyboru jednego lub drugiego bez martwienia się o to, czy w szablonach znajdują się skróty i / lub zmiany w interpretacji ścieżek względnych. Mam nadzieję, że twoja sztuczka pomoże sprawić, że „wszystko, co robisz, to zmiana trybu” (nawet jeśli praktycznym rozwiązaniem jest „ustaw tryb na html5, a następnie kłamać na temat możliwości przeglądarki”).
laurelnaiad

1
Dostaję się $providejest niezdefiniowany?
Petrus Theron

1
@pate - podczas konfigurowania dekoratora musisz wprowadzić $ $ do funkcji konfiguracji.
laurelnaiad

8

Przyszli czytelnicy przyszłości, jeśli używasz Angulara 1.6 , musisz również zmienić hashPrefix:

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

Nie zapomnij ustawić bazy w swoim HTML <head>:

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

Więcej informacji o dzienniku zmian here.


3
dzięki @Mistalis. twoja odpowiedź działa dobrze. ale problem pojawia się, gdy odświeżam stronę po routingu, występują błędy nie znaleziono strony. proszę pomóż mi ..
Ashish Mehta

@AshishMehta Hello Ashish. Sugeruję przeczytanie tej odpowiedzi , może to rozwiązać problem. PA! :)
Mistalis,

0

Zajęło mi to trochę czasu, żeby to rozgryźć, więc tak to działa - Angular WebAPI ASP Routing bez # dla SEO

  1. dodaj do Index.html - base href = "/">
  2. Dodaj $ locationProvider.html5Mode (prawda); do app.config

  3. Potrzebowałem pewnego kontrolera (który był w kontrolerze domowym), aby go zignorować podczas przesyłania obrazów, więc dodałem tę regułę do RouteConfig

         routes.MapRoute(
            name: "Default2",
            url: "Home/{*.}",
            defaults: new { controller = "Home", action = "SaveImage" }
        );
  4. W Global.asax dodaj następujące elementy - pamiętaj, aby zignorować api i ścieżki przesyłania obrazów, aby działały normalnie, w przeciwnym razie przekieruj wszystko inne.

     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. Upewnij się, że używasz $ location.url ('/ XXX'), a nie window.location ... do przekierowania

  6. Odwołaj się do plików CSS z bezwzględną ścieżką

i nie

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

Ostatnia uwaga - zrobienie tego w ten sposób dało mi pełną kontrolę i nie musiałem nic robić z konfiguracją sieci.

Mam nadzieję, że to pomaga, ponieważ zajęło mi to trochę czasu.


0

Chciałem mieć dostęp do mojej aplikacji w trybie HTML5 i stałym tokenie, a następnie przejść do metody hashbang (aby zatrzymać token, aby użytkownik mógł odświeżyć swoją stronę).

URL dostępu do mojej aplikacji:

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

Następnie, gdy użytkownik ładuje stronę:

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

Następnie, gdy użytkownik nawiguje:

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

Dzięki temu trzymam token w adresie URL i utrzymuję stan, gdy użytkownik przegląda. Straciłem trochę widoczność adresu URL, ale nie ma idealnego sposobu na zrobienie tego.

Więc nie włączaj trybu HTML5, a następnie dodaj ten kontroler:

.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' })
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.