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