使用ng-include保留传统锚定行为

时间:2022-04-02 22:51:02

I am not building a single-page application, but rather a "traditional" site that uses AngularJS in places. I've hit the following problem (using 1.3.0-beta.6):

我不是在构建单页应用程序,而是在某些地方使用AngularJS的“传统”站点。我遇到了以下问题(使用1.3.0-beta.6):

Standard, working anchor links:

标准的工作锚链:

<a href="#foo">Link text</a>
... [page content]
<a id="foo"></a>
<h1>Headline</h1>
[more content]

That works fine. Now I introduce a template partial somewhere:

这很好。现在我在某处介绍一个模板部分:

<script type="text/ng-template" id="test-include.html">
  <p>This text is in a separate partial and inlcuded via ng-include.</p>
</script>

which is invoked via:

通过以下方式调用:

<div ng-include="'test-include.html'"></div>

The partial is included properly, but the anchor link no longer works. Clicking on "Link text" now changes the displayed URL to /#/foo rather than /#foo and the page position does not change.

部分包含正确,但锚链接不再起作用。单击“链接文本”现在将显示的URL更改为/#/ foo而不是/#foo,页面位置不会更改。

My understanding is that using ng-include implicitly tells Angular that I want to use the routes system and overrides the browser's native anchor link behavior. I've seen recommendations to work around this by changing my html anchor links to #/#foo, but I can't do that for other reasons.

我的理解是使用ng-include隐式告诉Angular我想使用routes系统并覆盖浏览器的本机锚链接行为。我已经看到通过将我的html锚链接更改为#/#foo来解决此问题的建议,但由于其他原因我无法做到这一点。

I don't intend to use the routes system - I just want to use ng-include without it messing with browser behavior. Is this possible?

我不打算使用路由系统 - 我只想使用ng-include而不会弄乱浏览器行为。这可能吗?

5 个解决方案

#1


2  

My understanding is that using ng-include implicitly tells Angular that I want to use the routes system and overrides the browser's native anchor link behavior. I've seen recommendations to work around this by changing my html anchor links to #/#foo, but I can't do that for other reasons.

我的理解是使用ng-include隐式告诉Angular我想使用routes系统并覆盖浏览器的本机锚链接行为。我已经看到通过将我的html锚链接更改为#/#foo来解决此问题的建议,但由于其他原因我无法做到这一点。

Routing system is defined in a separate module ngRoute, so if you did not injected it on your own - and I am pretty sure you did not - it is not accessible at all.

路由系统是在一个单独的模块ngRoute中定义的,所以如果你没有自己注入它 - 而且我很确定你没有 - 它完全无法访问。

The issue is somehow different here.

这个问题在某种程度上是不同的。

ng-include depends on: $http, $templateCache, $anchorScroll, $animate, $sce. So make use of ng-include initiate all these services.

ng-include取决于:$ http,$ templateCache,$ anchorScroll,$ animate,$ sce。因此,使用ng-include启动所有这些服务。

The most natural candidate to investigate would be $anchorScroll. The code of $anchorScroll does not seem to do any harm, but the service depends on $window, $location, $rootScope. The line 616 of $location says:

调查最自然的候选人是$ anchorScroll。 $ anchorScroll的代码似乎没有任何损害,但服务依赖于$ window,$ location,$ rootScope。 $ location的616行说:

 baseHref = $browser.baseHref(), // if base[href] is undefined, it defaults to ''

So basically the base href is set to '', if it was no set before.

所以基本上,基本href设置为'',如果之前没有设置的话。

Now look HERE - from BalusC answer :

现在看看这里 - 来自BalusC回答:

As to using named anchors like , with the tag you're basically declaring all relative links relative to it, including named anchors. None of the relative links are relative to the current request URI anymore (as would happen without the tag).

至于使用命名锚点,使用标记,你基本上声明了相对于它的所有相对链接,包括命名锚点。相关链接都不再与当前请求URI相关(如果没有标记就会发生)。

How to mitigate the issue?

I do not have much time today, so cannot test it myself, but what I would try to check as the first option is to hook up to '$locationChangeStart' event and if the new url is of #xxxxxx type just prevent the default behaviour and scroll with $anchorScroll native methods instead.

我今天没有太多时间,所以不能自己测试,但我会尝试检查,因为第一个选项是挂钩'$ locationChangeStart'事件,如果新的url是#xxxxxx类型,只是防止默认行为并使用$ anchorScroll本机方法滚动。

Update

I think this code should do the work:

我认为这段代码应该做的工作:

$scope.$on("$locationChangeStart", function (event, next, current) {

    var el, elId;

    if (next.indexOf("#")>-1) {
        elId = next.split("#")[1];
        el = document.getElementById(elId);
        if(el){
           // el.scrollIntoView(); do not think we need it
           window.location.hash = "#" + elId;
           event.preventDefault();
        }    
    }
});

#2


6  

The reason is that angular overrides the behavior of standard HTML tags which include <a> also. I'm not sure when this change happened because angular v1.0.1 works fine with this.

原因是angular会覆盖包含的标准HTML标记的行为。我不确定这个改变何时发生,因为angular v1.0.1可以正常工作。

You should replace the href attribute with ngClick as:

您应该使用ngClick替换href属性:

<a ng-click="scroll()">Link text</a>

And in a controller so:

并在控制器中:

function MyCtrl($scope, $location, $anchorScroll) {
  $scope.scroll = function() {
    $location.hash('foo');
    $anchorScroll();
  };
};

Demo: http://jsfiddle.net/HB7LU/3261/show/

演示:http://jsfiddle.net/HB7LU/3261/show/

Or simply use double hash as:

或者简单地使用双哈希:

<a href='##foo'>Link text</a>

Demo: http://jsfiddle.net/HB7LU/3262/show/

演示:http://jsfiddle.net/HB7LU/3262/show/

Update: I did not know that you want no modification in HREF. But you can still achieve the desired result by overriding the existing a directive as:

更新:我不知道你不想修改HREF。但是,您仍然可以通过覆盖现有指令来实现所需的结果:

myApp.directive('a', function() {
  return {
    restrict: 'E', 
    link: function(scope, element) {
        element.attr('href', '#' + element.attr('href'));
    }
  };
});

Demo: http://jsfiddle.net/HB7LU/3263/

演示:http://jsfiddle.net/HB7LU/3263/

#3


2  

This is the best solution, and works in recent versions of Angular:

这是最好的解决方案,适用于Angular的最新版本:

Turn off URL manipulation in AngularJS

关闭AngularJS中的URL操作

#4


2  

A lot late to the party but I found that adding a simple target="_self" fixes it.

派对很晚但我发现添加一个简单的目标=“_ self”修复了它。

<a href="#anchor" target="_self">Link</a>

#5


1  

Rather than applying the angular application to the entire page, you can isolate the application to just the places you want to perform an ng-include. This will allow links outside the scope of the application to retain their normal functionality, while allowing links within the application to be handled as desired.

您可以将应用程序隔离到您要执行ng-include的位置,而不是将角度应用程序应用于整个页面。这将允许应用程序范围之外的链接保留其正常功能,同时允许应用程序内的链接按需处理。

See this plunkr:

看到这个plunkr:

http://plnkr.co/edit/hOB7ixRM39YZEhaz0tfr?p=preview

http://plnkr.co/edit/hOB7ixRM39YZEhaz0tfr?p=preview

The plunkr shows a link outside the app that functions as normal, and a link within the app that is handled using an overriding a directive to restore normal functionality. HTML5 mode is enabled to retain 'standard' URLs (rather than 'hashbang' [without the bang!] URLs).

plunkr在应用程序外部显示一个正常运行的链接,并在应用程序中显示一个链接,该链接使用覆盖指令来恢复正常功能。启用HTML5模式以保留“标准”URL(而不是'hashbang'[没有bang!] URL)。

You could equally run the whole of the page within the app, but I thought it would be worth demonstrating how to isolate angular to certain parts of the page in any case.

您可以同样在应用程序中运行整个页面,但我认为在任何情况下都需要演示如何将角度隔离到页面的某些部分。

#1


2  

My understanding is that using ng-include implicitly tells Angular that I want to use the routes system and overrides the browser's native anchor link behavior. I've seen recommendations to work around this by changing my html anchor links to #/#foo, but I can't do that for other reasons.

我的理解是使用ng-include隐式告诉Angular我想使用routes系统并覆盖浏览器的本机锚链接行为。我已经看到通过将我的html锚链接更改为#/#foo来解决此问题的建议,但由于其他原因我无法做到这一点。

Routing system is defined in a separate module ngRoute, so if you did not injected it on your own - and I am pretty sure you did not - it is not accessible at all.

路由系统是在一个单独的模块ngRoute中定义的,所以如果你没有自己注入它 - 而且我很确定你没有 - 它完全无法访问。

The issue is somehow different here.

这个问题在某种程度上是不同的。

ng-include depends on: $http, $templateCache, $anchorScroll, $animate, $sce. So make use of ng-include initiate all these services.

ng-include取决于:$ http,$ templateCache,$ anchorScroll,$ animate,$ sce。因此,使用ng-include启动所有这些服务。

The most natural candidate to investigate would be $anchorScroll. The code of $anchorScroll does not seem to do any harm, but the service depends on $window, $location, $rootScope. The line 616 of $location says:

调查最自然的候选人是$ anchorScroll。 $ anchorScroll的代码似乎没有任何损害,但服务依赖于$ window,$ location,$ rootScope。 $ location的616行说:

 baseHref = $browser.baseHref(), // if base[href] is undefined, it defaults to ''

So basically the base href is set to '', if it was no set before.

所以基本上,基本href设置为'',如果之前没有设置的话。

Now look HERE - from BalusC answer :

现在看看这里 - 来自BalusC回答:

As to using named anchors like , with the tag you're basically declaring all relative links relative to it, including named anchors. None of the relative links are relative to the current request URI anymore (as would happen without the tag).

至于使用命名锚点,使用标记,你基本上声明了相对于它的所有相对链接,包括命名锚点。相关链接都不再与当前请求URI相关(如果没有标记就会发生)。

How to mitigate the issue?

I do not have much time today, so cannot test it myself, but what I would try to check as the first option is to hook up to '$locationChangeStart' event and if the new url is of #xxxxxx type just prevent the default behaviour and scroll with $anchorScroll native methods instead.

我今天没有太多时间,所以不能自己测试,但我会尝试检查,因为第一个选项是挂钩'$ locationChangeStart'事件,如果新的url是#xxxxxx类型,只是防止默认行为并使用$ anchorScroll本机方法滚动。

Update

I think this code should do the work:

我认为这段代码应该做的工作:

$scope.$on("$locationChangeStart", function (event, next, current) {

    var el, elId;

    if (next.indexOf("#")>-1) {
        elId = next.split("#")[1];
        el = document.getElementById(elId);
        if(el){
           // el.scrollIntoView(); do not think we need it
           window.location.hash = "#" + elId;
           event.preventDefault();
        }    
    }
});

#2


6  

The reason is that angular overrides the behavior of standard HTML tags which include <a> also. I'm not sure when this change happened because angular v1.0.1 works fine with this.

原因是angular会覆盖包含的标准HTML标记的行为。我不确定这个改变何时发生,因为angular v1.0.1可以正常工作。

You should replace the href attribute with ngClick as:

您应该使用ngClick替换href属性:

<a ng-click="scroll()">Link text</a>

And in a controller so:

并在控制器中:

function MyCtrl($scope, $location, $anchorScroll) {
  $scope.scroll = function() {
    $location.hash('foo');
    $anchorScroll();
  };
};

Demo: http://jsfiddle.net/HB7LU/3261/show/

演示:http://jsfiddle.net/HB7LU/3261/show/

Or simply use double hash as:

或者简单地使用双哈希:

<a href='##foo'>Link text</a>

Demo: http://jsfiddle.net/HB7LU/3262/show/

演示:http://jsfiddle.net/HB7LU/3262/show/

Update: I did not know that you want no modification in HREF. But you can still achieve the desired result by overriding the existing a directive as:

更新:我不知道你不想修改HREF。但是,您仍然可以通过覆盖现有指令来实现所需的结果:

myApp.directive('a', function() {
  return {
    restrict: 'E', 
    link: function(scope, element) {
        element.attr('href', '#' + element.attr('href'));
    }
  };
});

Demo: http://jsfiddle.net/HB7LU/3263/

演示:http://jsfiddle.net/HB7LU/3263/

#3


2  

This is the best solution, and works in recent versions of Angular:

这是最好的解决方案,适用于Angular的最新版本:

Turn off URL manipulation in AngularJS

关闭AngularJS中的URL操作

#4


2  

A lot late to the party but I found that adding a simple target="_self" fixes it.

派对很晚但我发现添加一个简单的目标=“_ self”修复了它。

<a href="#anchor" target="_self">Link</a>

#5


1  

Rather than applying the angular application to the entire page, you can isolate the application to just the places you want to perform an ng-include. This will allow links outside the scope of the application to retain their normal functionality, while allowing links within the application to be handled as desired.

您可以将应用程序隔离到您要执行ng-include的位置,而不是将角度应用程序应用于整个页面。这将允许应用程序范围之外的链接保留其正常功能,同时允许应用程序内的链接按需处理。

See this plunkr:

看到这个plunkr:

http://plnkr.co/edit/hOB7ixRM39YZEhaz0tfr?p=preview

http://plnkr.co/edit/hOB7ixRM39YZEhaz0tfr?p=preview

The plunkr shows a link outside the app that functions as normal, and a link within the app that is handled using an overriding a directive to restore normal functionality. HTML5 mode is enabled to retain 'standard' URLs (rather than 'hashbang' [without the bang!] URLs).

plunkr在应用程序外部显示一个正常运行的链接,并在应用程序中显示一个链接,该链接使用覆盖指令来恢复正常功能。启用HTML5模式以保留“标准”URL(而不是'hashbang'[没有bang!] URL)。

You could equally run the whole of the page within the app, but I thought it would be worth demonstrating how to isolate angular to certain parts of the page in any case.

您可以同样在应用程序中运行整个页面,但我认为在任何情况下都需要演示如何将角度隔离到页面的某些部分。