使用Angular UI路由器在AngularJS中具有历史记录的一个页面上的多个视图

时间:2014-08-28 10:43:48

标签: javascript angularjs angular-ui-router

在我的AngularJS应用程序中,我有一个包含多个子部分的页面。所有这些子部分都在同一页面上,并且是同一模板的一部分。但是我想通过它自己的URL访问每个部分,如果它匹配,它将向下滚动到正确的部分。

我已将状态设置为:

.state('about', {
    url: "/about",
    templateUrl: "partials/about.html",
})
.state('about.team', {
    url: "/about/team",
    templateUrl: "partials/about.html"
})
.state('about.office', {
    url: "/about/office",
    templateUrl: "partials/about.html"
})
.state('about.other', {
    url: "/about/other",
    templateUrl: "partials/about.html"
});

在about页面上我有一个菜单:

<div class="menu">
    <a ui-sref-active="selected"
    ui-sref="about.team">The Team</a>
    <a ui-sref-active="selected"
    ui-sref="about.office">The Office</a>
    <a ui-sref-active="selected"
    ui-sref="about.other">Other</a>
</div>

此菜单固定在底部,当用户点击链接或通过地址栏直接访问网址时,它应滚动到该部分(更新需要匹配的网址)。

但是我不知道该怎么做(通过Angular)。总结一下:

  1. 如何通过网址滚动到同一页面上的不同部分?因为我不能使用标准哈希技巧,因为它已经使用哈希作为地址(我们支持IE8)。在HTML5模式下我根本不想使用哈希值! 它应该在应用程序加载后滚动到页面加载部分,因此需要一些方法来触发在其他内容之后滚动用户的代码...
  2. 如何点击按钮才能完成这项工作?目前,当单击按钮时,它们会更改URL,但不会滚动到内容。
  3. 页面的HTML看起来像:http://pastebin.com/Q60FWtL7

    您可以在此处查看该应用:http://dev.driz.co.uk/AngularPage/app/#/about

    对于类似的事情(https://devart.withgoogle.com/#/about/your-opportunity)的示例,您可以看到它根据网页加载后的网址向下滚动到正确的部分...并且不使用哈希但是实际网址

8 个答案:

答案 0 :(得分:2)

您可以尝试使用此方法创建一个触发单击滚动的指令。 您可以将此指令作为属性添加到链接中。其中sref属性将是目标div的id。

app.directive('scrollTo', function() {
  return {
    link: function(scope, element, attrs) {
    element.bind('click', function() {
      var divPosition = $(attrs.sRef).offset();
      $('html, body').animate({
      scrollTop: divPosition.top
      }, "slow");

    });
   }
  };
});

Plunker

答案 1 :(得分:2)

ui-router不对您的州对象的签名设置限制,因此您只需添加一个属性:

  .state('about.office', {
     url: "/about/office"
    ,templateUrl: "partials/about.html"
    ,scrollToId = "divAboutOffice";
  })

然后,当您注入$ state服务时,您的控制器可以访问此数据。

  $('html, body').animate({
    scrollTop:  $($state.current.scrollToId).offset().top
  }, "slow");

假设您在HTML中添加了id="divAboutOffice"。 (并假设我的jQuery引用是正确的;我不使用jquery所以我不确定。) 根据您的需要,您可以在animate()中将$on()换成其中一个事件:$locationChangeSuccess)$routeChangeSuccess$stateChangeSuccess

另外:您不必像我一样使用&#34; id&#34; - 如果您真的想按属性搜索元素,可以利用现有的ui-sref。在您看来匹配$state.current.name

的示例中

答案 2 :(得分:0)

我将使用window.scrollTo或jquery scrollTo插件创建一个scrollTo指令。

实施例: Angular directive to scroll to a given item 使用window.scrollTo

Jquery scrollTo插件:

https://github.com/flesler/jquery.localScroll

https://github.com/flesler/jquery.scrollTo

在页面加载时,滚动到由标签或任何其他url参数定义的视图。例: www.sample.com#aboutUs或www.samplecom?view = aboutUs

答案 3 :(得分:0)

滚动时,我应该:

1. bind to "scroll" events, 
2. compare if I reached the position of each headers, using "element.offset().top"

(在将此标题作为指令之后。(我应该将标题作为指令,检查如何从ngtutorial.com/learn/scope生成目录)

3. update location.url()

关于加载(我更喜欢使用hashtags,因为我可以使用$ anchorScroll()或使用.when()),无论如何加载时更新的示例是:

1. http://ngtutorial.com/guide.html#/learn-filter.html
2. http://ngtutorial.com/learn/route.html#/_tblcontents_5

如果您不喜欢主题标签,则可以解析location.url()

答案 4 :(得分:0)

我在自己的应用程序中使用https://github.com/durated/angular-scroll进行滚动控制(并滚动间谍),而不是使用locationHash。我自己没有尝试过这个解决方案,但我认为它会起作用。

您需要在重要的div上声明某种项ID(作为您滚动的位置)。

然后你可以使用url / about /:locale声明一个状态“about.stately”(在ui-router stateParams中不必是整数)。然后在控制器中约。最后,你可以(从角度滚动自述文件):

var someElement = angular.element(document.getElementById('some-id'));
$document.scrollToElement(someElement, offset, duration);

根据控制器中的$ stateParams.locale选择要滚动到的元素。小心等到你的所有子指令和东西加载后 - 你可能需要$ evalAsync才能使它正常工作。但我认为这就是它的主旨。

答案 5 :(得分:0)

这就是我现在所尝试的:

var scrolled = false;

var listenToScroll = false;

$('.subsection').each(function(i) {
    var position = $(this).position();
    $(this).scrollspy({
        min: position.top,
        max: position.top + $(this).outerHeight(),
        onEnter: function(element, position) {
            if(element.id){
                if(listenToScroll) {
                    scrolled = true;
                    $state.transitionTo('about.'+element.id);
                }
            } else {
                if(listenToScroll) {
                    scrolled = true;
                    $state.transitionTo('about');
                }
            }
        }
    });
});

$scope.startListenToScroll = function() {       

    listenToScroll = true;
}

$scope.stopListenToScroll = function(){

    listenToScroll = false;
}

$scope.$on('$stateChangeStart', function(){

    $scope.stopListenToScroll();

});

$scope.$on('$stateChangeSuccess', function(){

    console.log(scrolled);

    if( !scrolled ) {
        var link = $state.current.name.split('.');
        if(link.length>1){
            var divPosition = $( '#' + link[1] ).offset();
            $('body').stop(true,true).animate({ scrollTop: divPosition.top }, "fast", function(){
                $scope.startListenToScroll();
                scrolled = false;
            });
        } else {
            $('body').stop(true,true).animate({ scrollTop: 0 }, "fast", function(){
                $scope.startListenToScroll();
                scrolled = false;
            });
        }
    } else {
        $scope.startListenToScroll();
        scrolled = false;
    }

});

因此,您可以看到我收听stateChangeSuccess而不是点击!这样我就可以挂钩任何状态更改,无论它们是否被链接或浏览器历史记录按钮调用,并根据需要执行滚动。

一旦开始更改开始,我就会关闭scrollspy,以便滚动动画不会调用另一个状态更改(因为它可能会滚动其他部分创建虚假历史记录条目)。然后在动画完成后再次将其重新打开,这样我就可以再次收听滚动事件。

stateChangeSuccess在页面加载时触发,因此它处理任何URL粘贴。

我有一个名为scrolled的变量,以便我可以跟踪状态更改是否是由用户滚动引起的。这是因为当用户滚动它们时,我不需要为scrollTop设置动画。

<强>思考吗

答案 6 :(得分:0)

你需要做的是在页面顶部创建一个指令,除了监视状态的ui-router,然后根据右边的锚点滚动。因此,您无需使用templateUrls,因为您要实现的是某种平滑。

答案 7 :(得分:-1)

// get the current path
var x=$location.path();
// say x='about/team'
//read the last tag and set scrollTo to the top offset of desired div
if(lasttag(x)==team){
scroll to top offset of TEAM div;
}
else if(lasttag(x)==office){
scroll to top offset of OFFICE div;
}