$ location / Umschalten zwischen HTML5 und Hashbang-Modus / Link-Umschreiben

179

Ich hatte den Eindruck, dass Angular URLs, die in href-Attributen von Ankertags innerhalb von Tempaltes erscheinen, so umschreiben würde, dass sie im HTML5-Modus oder im Hashbang-Modus funktionieren würden. Die Dokumentation für den Ortungsdienst scheint zu sagen, dass HTML Link Rewriting sich um die Hashbang-Situation kümmert. Ich würde daher erwarten, dass Hashes eingefügt werden, wenn sie sich nicht im HTML5-Modus befinden, und im HTML5-Modus nicht.

Es scheint jedoch, dass kein Umschreiben stattfindet. Im folgenden Beispiel kann ich den Modus nicht einfach ändern. Alle Links in der Anwendung müssten von Hand neu geschrieben werden (oder zur Laufzeit von einer Variablen abgeleitet werden. Muss ich je nach Modus alle URLs manuell neu schreiben?

In Angular 1.0.6, 1.1.4 oder 1.1.3 wird keine clientseitige URL-Umschreibung durchgeführt. Es scheint, dass allen href-Werten # / für den Hashbang-Modus und / für den HTML5-Modus vorangestellt werden muss.

Ist eine Konfiguration erforderlich, um das Umschreiben zu veranlassen? Verstehe ich die Dokumente falsch? Etwas anderes Dummes tun?

Hier ist ein kleines Beispiel:

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

Nachtrag: Beim erneuten Lesen meiner Frage sehe ich, dass ich den Begriff "Umschreiben" verwendet habe, ohne viel Klarheit darüber zu haben, wer und wann ich das Umschreiben durchführen wollte. Die Frage ist, wie Angular dazu gebracht werden kann, die URLs beim Rendern von Pfaden neu zu schreiben, und wie Pfade im JS-Code in beiden Modi einheitlich interpretiert werden können. Es geht nicht darum, wie ein Webserver HTML5-kompatibles Umschreiben von Anforderungen veranlasst.

laurelnaiad
quelle
1
Hier ist die Lösung für Angular 1.6 .
Mistalis

Antworten:

361

Die Dokumentation zum AngularJS-Routing ist nicht sehr klar. Es geht um den Hashbang- und HTML5-Modus. Tatsächlich arbeitet AngularJS-Routing in drei Modi:

  • Hashbang-Modus
  • HTML5-Modus
  • Hashbang im HTML5-Modus

Für jeden Modus gibt es eine entsprechende LocationUrl-Klasse (LocationHashbangUrl, LocationUrl und LocationHashbangInHTML5Url).

Um das Umschreiben von URLs zu simulieren, müssen Sie html5mode auf true setzen und die $ sniffer-Klasse wie folgt dekorieren:

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

Ich werde dies nun genauer erklären:

Hashbang-Modus

Aufbau:

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

Dies ist der Fall, wenn Sie URLs mit Hashes in Ihren HTML-Dateien verwenden müssen, z

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

Im Browser müssen Sie den folgenden Link verwenden: http://www.example.com/base/index.html#!/base/path

Wie Sie im reinen Hashbang-Modus sehen können, müssen alle Links in den HTML-Dateien mit der Basis wie "index.html #!" Beginnen.

HTML5-Modus

Aufbau:

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

Sie sollten die Basis in HTML-Datei setzen

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

In diesem Modus können Sie Links ohne das # in HTML-Dateien verwenden

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

Link im Browser:

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

Hashbang im HTML5-Modus

Dieser Modus wird aktiviert, wenn wir tatsächlich den HTML5-Modus verwenden, jedoch in einem inkompatiblen Browser. Wir können diesen Modus in einem kompatiblen Browser simulieren, indem wir den $ sniffer-Dienst dekorieren und den Verlauf auf false setzen.

Aufbau:

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

Setze die Basis in HTML-Datei:

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

In diesem Fall können die Links auch ohne den Hash in der HTML-Datei geschrieben werden

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

Link im Browser:

http://www.example.com/index.html#!/base/path
Jupiter
quelle
Vielen Dank für die ausführliche Erklärung, @jupiter. Ich werde prüfen, ob ich den Knall überspringen und den Hash behalten und Angular dazu bringen kann, je nach Modus keine zwei Sätze von URLs zu benötigen!
Laurelnaiad
1
Ich glaube, ich habe Ihr Problem nicht richtig verstanden. Warum verwenden Sie nicht nur URLs ohne Hash? Sie funktionieren in Browsern, die die Verlaufs-API unterstützen, und in Browsern, die die Verlaufs-API nicht unterstützen. AngularJS fügt die # -Version in die Adressleiste ein, wenn Sie in Browsern darauf klicken, die die Verlaufs-API nicht unterstützen, da AngularJS Klicks auf Links abfängt.
Jupiter
Ich arbeite an einem Framework, das beide Modi unterstützen soll. App-Autoren sollten in der Lage sein, das eine oder das andere auszuwählen, ohne sich Gedanken darüber zu machen, ob ihre Vorlagen Hashes und / oder Änderungen bei der Interpretation der relativen Pfade enthalten. Ich hoffe, dass Ihr Trick dazu beiträgt, dass "alles, was Sie tun, ist, den Modus zu ändern" (selbst wenn die praktische Lösung darin besteht, "den Modus auf HTML5 zu setzen und dann über die Browserfunktionen zu lügen").
Laurelnaiad
1
Ich bekomme $provideist undefiniert?
Petrus Theron
1
@pate - Sie müssen $ deploy in Ihre Konfigurationsfunktion einfügen, wenn Sie den Dekorator einrichten.
Laurelnaiad
8

Für zukünftige Leser, wenn Sie Angular 1.6 verwenden , müssen Sie auch Folgendes ändern hashPrefix:

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

Vergessen Sie nicht, die Basis in Ihrem HTML festzulegen <head>:

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

Weitere Infos zum Changelog here.

Mistalis
quelle
3
danke @Mistalis. Ihre Antwort funktioniert gut. Wenn ich jedoch die Seite nach dem Routing aktualisiere, treten Fehler auf, bei denen die Seite nicht gefunden wurde. Bitte helfen Sie mir ..
Ashish Mehta
@ AshishMehta Hallo Ashish. Ich empfehle Ihnen, diese Antwort zu lesen, da dies Ihr Problem lösen kann. Tschüss! :)
Mistalis
0

Es hat eine Weile gedauert, bis ich herausgefunden habe, wie ich es zum Laufen gebracht habe - Angular WebAPI ASP Routing ohne das # für SEO

  1. zu Index.html hinzufügen - base href = "/">
  2. Add $ locationProvider.html5Mode (true); zu app.config

  3. Ich brauchte einen bestimmten Controller (der sich im Home-Controller befand), der beim Hochladen von Bildern ignoriert werden musste, also habe ich diese Regel zu RouteConfig hinzugefügt

         routes.MapRoute(
            name: "Default2",
            url: "Home/{*.}",
            defaults: new { controller = "Home", action = "SaveImage" }
        );
  4. Fügen Sie in Global.asax Folgendes hinzu: Wenn Sie die API- und Bild-Upload-Pfade ignorieren, funktionieren sie wie gewohnt, andernfalls leiten Sie alles andere um.

     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. Stellen Sie sicher, dass Sie $ location.url ('/ XXX') und nicht window.location ... verwenden, um umzuleiten

  6. Verweisen Sie auf die CSS-Dateien mit dem absoluten Pfad

und nicht

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

Letzte Anmerkung - auf diese Weise hatte ich die volle Kontrolle und musste nichts an der Webkonfiguration tun.

Ich hoffe, das hilft, da ich eine Weile gebraucht habe, um das herauszufinden.

tfa
quelle
0

Ich wollte in der Lage sein, mit dem HTML5-Modus und einem festen Token auf meine Anwendung zuzugreifen und dann zur Hashbang-Methode zu wechseln (um das Token beizubehalten, damit der Benutzer seine Seite aktualisieren kann).

URL für den Zugriff auf meine App:

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

Wenn der Benutzer die Seite lädt:

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

Wenn der Benutzer dann navigiert:

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

Damit behalte ich das Token in der URL und den Status, wenn der Benutzer surft. Ich habe die Sichtbarkeit der URL ein wenig verloren, aber es gibt keine perfekte Möglichkeit, dies zu tun.

Aktivieren Sie also nicht den HTML5-Modus und fügen Sie dann diesen Controller hinzu:

.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' })
Luc Boissaye
quelle