Multiple views on the same story page in AngularJS using Angular UI Router

In my AngularJS application, I have a page with several subsections. All of these subsections are on the same page and are part of the same template. However, I want to access each section through its own URL, which will scroll to the desired section if it matches.

I set the states like this:

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

 <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 on the link or when visiting the URL directly through the address bar, he should scroll to this section (updating the URL where necessary for compliance).

However, I'm not sure how to do this (via Angular). Summarizing:

  • How to scroll pages on one page using url? Since I cannot use the standard hash trick, since it already uses hashes for the address (we support IE8). And I don't want to use hashes at all when in HTML5 mode! It should also go to the download section of the AFTER application download, so you need to somehow run the code that scrolls the user after something else ...
  • How can I do this work by pressing buttons? Currently, when you click on the buttons, they change the URL, but do not scroll to the content.

The HTML for the page looks like: http://pastebin.com/Q60FWtL7

Here you can see the application: http://dev.driz.co.uk/AngularPage/app/#/about

An example of something similar ( https://devart.withgoogle.com/#/about/your-opportunity ), since you can see that it scrolls to the desired section based on url AFTER the page loads ... and does not use a hash, except for the ACTUAL URL.

+8
javascript angularjs angular-ui-router
source share
8 answers

You can try this approach when you create a directive that launches click-through scrolling. You can add this directive to your link as an attribute. where the sref attribute will be the identifier of the target div.

 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

+2
source share

ui-router does not set signature restrictions on your state object, so you can simply add a property:

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

Then your controller can access this data when you enter $ state service.

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

if you included id="divAboutOffice" in your HTML. (and assuming my jQuery link is correct, I am not using jquery, so I'm not sure). Depending on your needs, you can conclude that animate() in $on() for one of the events: $locationChangeSuccess) , $routeChangeSuccess or $stateChangeSuccess

also: you do not need to use "id" like me. You can use existing ui-sref if you really want to search for elements by attribute. In your example, that matches $state.current.name

+2
source share

I would create a scrollTo directive using window.scrollTo or jquery scrollTo Plugin.

Example: Angular to scroll to a specific item using window.scrollTo

Jquery scrollTo Plugins:

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

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

When the page loads, you look at the view defined by the hashtag or any other url parameter. Example: www.sample.com # aboutUs or www.samplecom? view = aboutUs

0
source share

when scrolling, I have to:

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

(after creating these directive headers. (I have to make the headers directive, check how the table of contents is created using ngtutorial.com/learn/scope)

 3. update location.url() 

at boot (I prefer to use hashtags because I can just use $ anchorScroll () or use .when ()), any examples of updates at boot:

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

0
source share

I use https://github.com/durated/angular-scroll in my application to control scrolling (and scrolling spy stuff) instead of using locationHash. I have not tried this solution myself, but I think it will work.

You need to declare some element identifier on important divs (to be the place you scroll).

You can then declare the state "about.stately" with url / about /: locale (in ui-router stateParams do not have to be integers). Then in the controller for about.stately you can do (from angular -scroll readme):

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

Choose which element you scroll based on $ stateParams.locale in your controller. Be careful, wait until after all the directives of your child and the load on the material - you may need $ evalAsync to make it work correctly. But, apparently, the essence of this.

0
source share

This is what I 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'm listening to stateChangeSuccess instead of clicks! This way, I intercept any state changes, regardless of whether they were triggered by links or browser history buttons, and scroll as needed.

I will turn off scrollspy as soon as the initial change begins, so that the scroll animation will not cause another state change (since you can scroll THROUGH other sections creating fake history entries). And then turn it back on when the animation is complete to listen to the scroll events again.

stateChangeSuccess fires when the page loads, so it handles any URL attachment.

I have a variable called scrolled , so I can keep track of whether the change in state was caused by a user scroll or not. This is because I do not need to animate scrollTop when the user scrolls through them.

Thoughts?

0
source share

what you need to do is create a directive at the top of the page, except for ui-router, which monitors the state and then scrolls according to the correct anchor. Then there is no need to use templateUrls, because what you are trying to achieve is smoothing.

0
source share
 // 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; } 
-one
source share

All Articles