$ location / passaggio tra modalità html5 e hashbang / riscrittura dei collegamenti


179

Avevo l'impressione che Angular avrebbe riscritto gli URL che compaiono negli attributi href dei tag anchor all'interno di tempaltes, in modo tale che funzionino sia in modalità html5 che hashbang. La documentazione per il servizio di localizzazione sembra indicare che HTML Link Rewriting si occupa della situazione di hashbang. Mi aspetterei quindi che quando non fossero in modalità HTML5, gli hash sarebbero stati inseriti, e in modalità HTML5 no.

Tuttavia, sembra che non sia in corso alcuna riscrittura. L'esempio seguente non mi consente di cambiare la modalità. Tutti i collegamenti nell'applicazione dovrebbero essere riscritti a mano (o derivati ​​da una variabile in fase di esecuzione. Devo riscrivere manualmente tutti gli URL a seconda della modalità?

Non vedo alcuna riscrittura degli URL lato client in corso in Angular 1.0.6, 1.1.4 o 1.1.3. Sembra che tutti i valori di href debbano essere anteposti con # / per la modalità hashbang e / per la modalità html5.

C'è qualche configurazione necessaria per causare la riscrittura? Sto leggendo male i documenti? Fare qualcos'altro sciocco?

Ecco un piccolo esempio:

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

Addendum: nel rileggere la mia domanda, vedo che ho usato il termine "riscrittura" senza un'abbondanza di chiarezza su chi e quando volevo fare la riscrittura. La domanda è su come fare in modo che Angular riscriva gli URL quando esegue il rendering dei percorsi e come farlo interpretare i percorsi nel codice JS in modo uniforme tra le due modalità. Si tratta non di come per causare un server web per fare compatibile con HTML5 riscrittura delle richieste.


1
Ecco la soluzione per Angular 1.6 .
Mistalis

Risposte:


361

La documentazione non è molto chiara sul routing AngularJS. Parla della modalità Hashbang e HTML5. In effetti, il routing AngularJS funziona in tre modalità:

  • Modalità Hashbang
  • Modalità HTML5
  • Hashbang in modalità HTML5

Per ogni modalità esiste una rispettiva classe LocationUrl (LocationHashbangUrl, LocationUrl e LocationHashbangInHTML5Url).

Per simulare la riscrittura degli URL devi effettivamente impostare html5mode su true e decorare la classe $ sniffer come segue:

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

Ora lo spiegherò in modo più dettagliato:

Modalità Hashbang

Configurazione:

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

Questo è il caso in cui è necessario utilizzare URL con hash nei file HTML come in

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

Nel browser è necessario utilizzare il seguente collegamento: http://www.example.com/base/index.html#!/base/path

Come puoi vedere nella pura modalità Hashbang, tutti i collegamenti nei file HTML devono iniziare con la base come "index.html #!".

Modalità HTML5

Configurazione:

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

È necessario impostare la base in file HTML

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

In questa modalità è possibile utilizzare i collegamenti senza il # nei file HTML

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

Collegamento nel browser:

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

Hashbang in modalità HTML5

Questa modalità è attivata quando utilizziamo effettivamente la modalità HTML5 ma in un browser incompatibile. Possiamo simulare questa modalità in un browser compatibile decorando il servizio $ sniffer e impostando la cronologia su false.

Configurazione:

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

Imposta la base nel file HTML:

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

In questo caso i collegamenti possono anche essere scritti senza l'hash nel file HTML

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

Collegamento nel browser:

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

Grazie per la spiegazione dettagliata, @jupiter. Vedrò se posso saltare il botto e mantenere l'hash e indurre Angular a non richiedere due set di URL a seconda della modalità!
laurelnaiad,

1
Penso di non aver capito bene il tuo problema. Perché non usi solo URL senza hash? Funzioneranno con browser che supportano l'API di cronologia e browser che non supportano l'API di cronologia. AngularJS inserirà la versione # nella barra degli indirizzi quando fai clic su di essa nei browser che non supportano l'API della cronologia poiché AngularJS intercetta i clic sui collegamenti.
giove

Sto lavorando a un framework che dovrebbe supportare entrambe le modalità. Gli autori di app dovrebbero essere in grado di scegliere l'uno o l'altro senza preoccuparsi se ci sono hash nei loro modelli e / o cambiamenti nell'interpretazione dei percorsi relativi. Spero che il tuo trucco contribuisca a rendere vero che "tutto ciò che fai è cambiare la modalità" (anche se la soluzione pratica è "imposta la modalità su html5 e quindi mentire in modo angolare sulle funzionalità del browser").
laurelnaiad,

1
Sto diventando $provideindefinito?
Petrus Theron,

1
@pate - devi inserire $ fornire nella tua funzione di configurazione quando stai configurando il decoratore.
laurelnaiad,

8

Per i futuri lettori, se stai usando Angular 1.6 , devi anche cambiare hashPrefix:

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

Non dimenticare di impostare la base nel tuo HTML <head>:

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

Maggiori informazioni sul log delle modifiche here.


3
grazie @Mistalis. la tua risposta funziona bene. ma problema quando aggiorno la pagina dopo il routing, si verificano errori di pagina non trovata. per favore aiutami ..
Ashish Mehta,

@AshishMehta Hello Ashish. Ti consiglio di leggere questa risposta , potrebbe risolvere il tuo problema. Ciao! :)
Mistalis,

0

Mi ci è voluto un po 'di tempo per capire, quindi è così che l'ho fatto funzionare - Angular WebAPI ASP Routing senza il # per SEO

  1. aggiungi a Index.html - base href = "/">
  2. Aggiungi $ locationProvider.html5Mode (true); su app.config

  3. Avevo bisogno di un certo controller (che si trovava nel controller di casa) da ignorare per il caricamento delle immagini, quindi ho aggiunto quella regola a RouteConfig

         routes.MapRoute(
            name: "Default2",
            url: "Home/{*.}",
            defaults: new { controller = "Home", action = "SaveImage" }
        );
  4. In Global.asax aggiungi quanto segue - assicurandoti di ignorare i percorsi di caricamento delle API e delle immagini per farli funzionare normalmente altrimenti reindirizza tutto il resto.

     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. Assicurati di usare $ location.url ('/ XXX') e non window.location ... per reindirizzare

  6. Fare riferimento ai file CSS con percorso assoluto

e non

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

Nota finale: farlo in questo modo mi ha dato il pieno controllo e non avevo bisogno di fare nulla per la configurazione web.

Spero che questo aiuti, poiché mi ci è voluto un po 'di tempo per capire.


0

Volevo essere in grado di accedere alla mia applicazione con la modalità HTML5 e un token fisso e quindi passare al metodo hashbang (per mantenere il token in modo che l'utente possa aggiornare la sua pagina).

URL per accedere alla mia app:

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

Quindi quando l'utente carica la pagina:

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

Quindi quando l'utente naviga:

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

Con questo, mantengo il token nell'URL e mantengo lo stato quando l'utente sta navigando. Ho perso un po 'di visibilità dell'URL, ma non esiste un modo perfetto per farlo.

Quindi non abilitare la modalità HTML5 e quindi aggiungere questo controller:

.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' })
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.