从 Angular 中删除 hashchange 事件,或防止 Angular 重写锚链接

Remove hashchange event from Angular, or prevent Angular from rewriting anchor links

我们有一个使用 Angular 的项目,但仅用于它的 UI binding/AJAX 方面,不用于任何类型的路由或 SPA 功能。

我们希望能够在我们选择的 CMS 中撰写的文章中使用锚点 links (#section-2),以及使用来自其他网站的锚点 links页面 (/my-page#section-C),但 Angular 将这些重写为 #/section-2,这破坏了 CMS 设置的锚点 link。

无法扩充 CMS 来修改锚 link 的处理方式。

是否可以:

  1. 从 Angular 中删除 hashchange 事件绑定?我看到这个事件附加在源文件 src/ng/browser.js 中,它处理一些路由和 link 重写,但它在闭包内部所以不能直接访问(我们是 linking 到 Angular 从 CDN 所以不可能修改 Angular 源,而且我们不想维护我们自己的 "custom" Angular来源).

  2. 设置一个选项或调用一个配置方法,最终禁用 Angular 的整个路由方面并防止它重写任何类型的 link? (或者,有没有办法不包含 Angular 的这一部分,但仍然保留 controller/UI binding/AJAX 的功能?)

注意我已经试过了:

$locationProvider.html5Mode(true)

然而,它会导致站点上的所有其他 link 无法运行,因为所有 link 都通过 Angular 进行处理。因此,如果我 link 到主页 (<a href="/">Home</a>) 并单击带有 html5mode 的 link,link 什么都不做。

我相信你想要$anchorScroll. See this related answer: How to handle anchor hash linking in AngularJS

这是一个如何工作的例子。哈希仅被视为 id 的一部分:

app.controller('TestCtrl', function($location, $anchorScroll) {
   var vm = this;
   vm.scrollTo = function(id) {
      $location.hash(id);
      $anchorScroll();
   }
});

<a ng-click="vm.scrollTo('#foo')">Foo</a>

<div id="#foo">Here you are</div>

plunker demonstrating $anchorScroll

有了路由,你可以把link改成:

<a href="#/test##foo">Test/Foo</a>

并将其添加到 运行 配置中:

 $rootScope.$on('$routeChangeSuccess', function(newRoute, oldRoute) {
    if($location.hash()) {
        $anchorScroll();  
    }
 });

plunker demonstrating scrolling with routing and $anchorScroll

作为一种解决方法(当然不是最佳实践),我最终修改了 Angular 源代码以删除 URL 重写。

我做了一些更改,但我相信导致锚链接再次工作的是在 Angular 来源的 location.js 的第 844 行添加了 return; 语句:

https://github.com/angular/angular.js/blob/master/src/ng/location.js#L844

这绕过了大部分 URL 重写功能。

我还完全删除了 browser.js 的第 262-264 行,这删除了 ​​Angular 对 hashchange 事件的挂钩:

https://github.com/angular/angular.js/blob/master/src/ng/browser.js#L262-264

这似乎没有影响 Angular 的任何绑定功能,但确实导致锚链接再次开始工作。

但是为什么不用ng-route呢?它会完全解决你的问题。使用 Ng 路由你可以实现以下(这就是你想要的)。

<a href="http://www.example.com/base/#section-A"> Section A</a>

<a href="http://www.example.com/base/my-page#section-C">Another page</a>

<a href="/other-base/another?search">external</a>

这是 app.js 片段

app.config(function($locationProvider) {
  $locationProvider.html5Mode(true).hashPrefix('!');
})

Html link重写

当您使用 HTML5 历史记录 API 模式时,您将不需要特殊的 hashbang links。您所要做的就是指定常规 URL link,例如:<a href="/some?foo=bar">link</a>

当用户点击此 link,

在旧版浏览器中,URL 更改为 /index.html#!/some?foo=bar

在现代浏览器中,URL 变为 /some?foo=bar

像下面这样的情况,link不会被重写;相反,浏览器将执行完整的页面重新加载到原始 link.

包含目标元素的链接 示例:<a href="/ext/link?a=b" target="_self">link</a>

转到不同域的绝对 link 示例:<a href="http://angularjs.org/">link</a>

以“/”开头的指向不同基本路径的链接 示例:<a href="/not-my-base/link">link</a>

相对links

务必检查所有相关的 links、图像、脚本等。Angular 要求您在主 html 的头部指定 url 基文件 (<base href="/my-base/index.html">),除非在传递给 $locationProvider.html5Mode() 的 html5Mode 定义对象中将 html5Mode.requireBase 设置为 false。这样,即使文档的初始 url 不同,相对 urls 也将始终解析为该基础 url。

有一个例外:仅包含散列片段的链接(例如 <a href="#target">)只会更改 $location.hash() 而不会修改 url。这对于滚动到同一页面上的锚点很有用,而无需知道用户当前在哪个页面上。

服务器端

使用此模式需要在服务器端进行 URL 重写,基本上您必须将所有 link 重写为应用程序的入口点(例如 index.html)。对于这种情况,需要标记也很重要,因为它允许 Angular 区分 url 的部分,即应用程序基础和应由应用程序处理的路径。

ngSource

在设置$locationProvider.html5Mode(true)后,您是否也在文档的<head>中设置了一个<base>

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

看看here:

“有两件事需要完成。

  1. 正在配置 $locationProvider
  2. 设置相对链接的基础

here

我在将 angular 与 wordpress 一起使用时遇到了类似的问题,并找到了一个非常简单的修复方法。您所要做的就是删除控制器中的所有 $location 注入。当您注入 $location 服务时(无论您是否使用它),它都会劫持 # 行为。

这意味着您必须仔细检查所有控制器、指令等,并确保 $location 服务没有被注入到任何地方。

HTH

Angular 的 $locationProvider.html5Mode function with it's rewriteLinks 设置正是您要找的。它会将 angular 置于 $location 仍可用于读取和写入浏览器的 url 的模式,但它永远不会尝试劫持 link 点击并触发 angular的SPA路由。

例如:

  $locationProvider.html5Mode({
      enabled: true,
      requireBase: false,
      rewriteLinks: false 
  });