简体   繁体   English

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

[英]Multiple views on one page with history in AngularJS using Angular UI Router

In my AngularJS app, I have an about page with several sub-sections. 在我的AngularJS应用中,我有一个关于页面,其中包含几个小节。 All these sub-sections are on the same page and part of the same template. 所有这些小节都在同一页面上,并且是同一模板的一部分。 However I want to access each section via it's own URL that will scroll down to the correct section if it matches. 但是,我想通过它自己的URL访问每个部分,如果匹配,它将向下滚动到正确的部分。

I've set up the states as so: 我已经将状态设置为:

.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"
});

And on the about page I have a menu like: 在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>

This menu is fixed at the bottom and when the user clicks the link or when visiting the url direct via the address bar it should scroll to that section (updating the url where need be to match it). 此菜单固定在底部,当用户单击链接或通过地址栏直接访问URL时,应滚动到该部分(在需要匹配的URL上进行更新)。

However I'm not sure how to do this (via Angular). 但是我不确定如何做到这一点(通过Angular)。 To summarize: 总结一下:

  1. How can I scroll to the different sections on the same page via the URL? 如何通过URL滚动到同一页面的不同部分? As I can't use the standard hash trick as it already uses hashes for the address (we support IE8). 由于我无法使用标准的哈希技巧,因为它已经在地址中使用了哈希(我们支持IE8)。 And I don't want to use hashes at all when in HTML5 mode! 而且,在HTML5模式下,我根本不想使用哈希! It should also scroll to the section on page load AFTER the app has loaded, so need some way of firing the code that scrolls the user after something else... 应用加载后,它还应该滚动到页面加载部分,因此需要一种触发代码的方法,以便在其他情况下滚动用户。
  2. How can I make this work on click of the buttons? 单击按钮如何进行这项工作? Currently when clicking the buttons, they change the URL but don't scroll to the content. 当前,当单击按钮时,它们会更改URL,但不会滚动到内容。

The HTML for the page looks Like: http://pastebin.com/Q60FWtL7 该页面的HTML看起来像: http : //pastebin.com/Q60FWtL7

You can see the app here: http://dev.driz.co.uk/AngularPage/app/#/about 您可以在此处查看该应用程序: http : //dev.driz.co.uk/AngularPage/app/#/about

For an example of something similar ( https://devart.withgoogle.com/#/about/your-opportunity ) as you can see it scrolls down to the correct section based on the url AFTER the page has loaded... and doesn't use a hash but the ACTUAL url. 有关类似示例( https://devart.withgoogle.com/#/about/your-opportunity )的示例,您可以看到页面加载后,它会根据URL向下滚动到正确的部分。不要使用哈希,而是使用ACTUAL网址。

You can try this approach where you create a directive which triggers the scroll on click. 您可以在创建可触发点击滚动的指令时尝试这种方法。 You can add this directive to your link as an attribute. 您可以将此指令作为属性添加到链接。 where the sref attribute will be the id of the target div. 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 柱塞

ui-router doesn't place restrictions on the signature of your state object, so you can simply add a property: ui-router对状态对象的签名没有限制,因此您只需添加一个属性即可:

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

Then your controller can access this data when you inject the $state service. 这样,当您注入$ state服务时,控制器就可以访问此数据。

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

assuming you included the id="divAboutOffice" in your HTML. 假设您在HTML中添加了id="divAboutOffice" (and assuming my jQuery reference was correct; I don't use jquery so I'm not sure.) Depending on your needs, you would wrap that animate() in an $on() for one of the events: $locationChangeSuccess) , $routeChangeSuccess , or $stateChangeSuccess (并且假设我的jQuery引用是正确的;我不使用jquery,所以我不确定。)根据您的需要,您可以将animate()包装在$on()用于以下事件之一: $locationChangeSuccess)$routeChangeSuccess$stateChangeSuccess

also: You don't have to use the "id", as I have - You could leverage the existing ui-sref if you really want to search for elements by attribute. 还:您不必像我一样使用“ id”-如果您确实想按属性搜索元素,则可以利用现有的ui-sref In your example that appears to match $state.current.name 在您的示例中,看起来与$state.current.name匹配

I would create a scrollTo directive using window.scrollTo or a jquery scrollTo Plugin. 我将使用window.scrollTo或jquery scrollTo插件创建scrollTo指令。

Example: Angular directive to scroll to a given item using window.scrollTo 示例:使用window.scrollTo 滚动到给定项目的Angular指令

Jquery scrollTo Plugins: jQuery scrollTo插件:

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

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

On Page loading you scroll to the View defined by the hashtag or any other url parameter. 在页面加载中,您滚动到由标签或任何其他url参数定义的视图。 Example: www.sample.com#aboutUs Or www.samplecom?view=aboutUs 例如:www.sample.com#aboutUs或www.samplecom?view = aboutUs

on scrolling, i should: 在滚动时,我应该:

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

(after making this headers a directive. (i should make headers a directive, check how table of contents are generated from ngtutorial.com/learn/scope) (在将标题设置为指令后。(我应将标题设置为指令,检查如何从ngtutorial.com/learn/scope生成目录)

3. update location.url()

on loading (i prefer using hashtags, because I can just use $anchorScroll() or use .when()), anyways examples on updating when loading are: 在加载时(我更喜欢使用标签,因为我可以只使用$ anchorScroll()或.when()),无论如何,在加载时进行更新的示例是:

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

if you don't like hashtags, you can parse location.url() 如果您不喜欢主题标签,则可以解析location.url()

I use https://github.com/durated/angular-scroll in my own application to do scrolling control (and scroll spy stuff), instead of using locationHash. 我在自己的应用程序中使用https://github.com/durated/angular-scroll进行滚动控制(和滚动间谍工作),而不是使用locationHash。 I've not tried this solution myself, but I reckon it'll work. 我自己没有尝试过此解决方案,但我认为它可以工作。

You'll want to declare some kind of item id on important divs (to be the place you scroll to). 您需要在重要的div上声明某种项目ID(作为您滚动到的位置)。

Then you can declare a state "about.stately" with a url /about/:locale (in ui-router stateParams don't have to be integers). 然后,您可以使用url / about /:locale声明状态“ about.stately”(在ui路由器state中,Param不必为整数)。 Then in the controller for about.stately, you can do (from the angular-scroll readme): 然后在控制器中大约。状态,您可以(从角度滚动自述文件中)执行以下操作:

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

Pick which element you're scrolling to based on the $stateParams.locale in your controller. 根据控制器中的$ stateParams.locale选择要滚动到的元素。 Be careful to wait until after all your child directives and stuff load - you may have to $evalAsync to get it to work properly. 请注意,直到所有子指令和所有内容加载完毕后,您可能需要$ evalAsync才能使其正常工作。 But that's the gist of it, I think. 但是,我认为这就是要点。

This is what I have tried at the moment: 这是我目前所尝试的:

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

});

So as you can see I listen to the stateChangeSuccess instead of clicks! 如您所见,我听stateChangeSuccess而不是点击! That way I hook into any state changes regardless if they were called by links or browser history buttons and perform the scroll as need be. 这样,无论钩子是由链接还是由浏览器历史记录按钮调用的,我都会陷入任何状态更改,并根据需要执行滚动。

I turn scrollspy off as soon as a start change starts, so that the scrolling animation doesn't call another state change (as it's possible you will scroll THROUGH other sections creating fake history entries). 我会在开始更改开始后立即关闭scrollspy,以便滚动动画不会调用其他状态更改(因为您可能会通过其他部分进行滚动,从而创建伪造的历史记录条目)。 And then switch it back on again when the animation is complete so I can listen for scroll events again. 动画完成后再将其重新打开,这样我就可以再次收听滚动事件。

stateChangeSuccess is fired on page load so it handles any pasting of URLs. stateChangeSuccess在页面加载时触发,因此它可以处理任何URL粘贴。

I have a variable called scrolled so that I can track if a state change was caused by the user scrolling or not. 我有一个名为scrolled的变量,因此我可以跟踪状态更改是否由用户滚动引起。 This is because I don't need to animate the scrollTop as the user has scrolled themseleves. 这是因为当用户滚动它们时,我不需要为scrollTop设置动画。

Thoughts? 有什么想法吗?

what you need to do is create a directive at the top of the page besides the ui-router that watches the state and then scrolls according to the right anchor. 您需要做的是在ui-router之外的页面顶部创建一个指令,该指令可以监视状态,然后根据右锚点滚动。 There is no need then to use templateUrls since what you are trying to achieve is some kind of smoothing. 然后,您无需使用templateUrls,因为您要实现的是某种平滑。

// 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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM