Eolis
Eolis

Reputation: 655

AngularJS: Auto-scroll to anchor point after state change

I am looking to have a user click a link on a page, this will trigger a stateChange and lead them to a new state. what I want to achieve is when the state is finished loading it then scrolls to an anchor on the page specified by the link on the previous page.

to Do this I am using a combination of passing a $stateParam through a ui-sref like so:

<a ui-sref="stateParent.stateChild({id: 'practiceParam'})">goToPage</a>

then when the page is reached there is a div sitting on it with a directive attached that activates on $viewContentLoaded, so the DOM is rendered and I can search for an ID. the HTML div looks like this:

<div scroll-after-load ></div>

and my scrolling directive is as follows:

angular.module( 'app' ).directive('scrollAfterLoad', function() {
  return {
    restrict: 'A',
    link: function(scope, $elm, attrs, $stateParams) {
        scope.$on('$viewContentLoaded', function() {
            console.log('scrollAfterLoad Directive Loaded, $stateParams are: ', $stateParams );
            var idToScroll = attrs.href;
            var $target;
            if (idToScroll) {
                $target = $(idToScroll);
                // the -50 accounts for the top navbar which is fixed on the page and removed from pageflow
                $("html,body").animate({scrollTop: $target.offset().top - 50}, "slow");
            }
        });
    }
  };
});

I have not bothered with setting the href on the div yet as I cannot access the passed parameter, am I accessing it incorrectly? I have also tried using a more standard state.go() to pass a stateParam but my result is still null.

Once I can pass a stateparam the Idea is just to add an href to the params that gets injected into the div on the newly loaded page and autoscroll to another div on that page with an ID that matches my passed parameter.

Also a side note, on my ui-view's i have autoscroll set to true so the pages automatically load at the top, I like this behaviour and it is the reason I need the state to complete loading before the scoll is activated.

Upvotes: 3

Views: 4713

Answers (2)

LordTribual
LordTribual

Reputation: 4249

Your problem is that you are trying to inject the $stateParams service into the link function. However, the correct place for this kind of injection is at the factory method, means the definition of the directive. A factory method is registered with a module - as in your case with the scrollAfterLoad directive. For more information check AngularJS Dependency Injection.

So back to your question: If you actually inject $stateParams into your factory method of your directive, means at the top you will be able to easily access the params. Your directive could look like this:

angular.module( 'app' ).directive('scrollAfterLoad', ['$stateParams', function($stateParams) {
  return {
    restrict: 'A',
    link: function(scope, $elm, attrs) {
        scope.$on('$viewContentLoaded', function() {
            console.log('scrollAfterLoad Directive Loaded, $stateParams are: ', $stateParams );
            var idToScroll = attrs.href;
            var $target;
            if (idToScroll) {
                $target = $(idToScroll);
                // the -50 accounts for the top navbar which is fixed on the page and removed from pageflow
                $("html,body").animate({scrollTop: $target.offset().top - 50}, "slow");
            }
        });
    }
  };
}]);

Notice that I have removed the $stateParams from your link function and put it at the top. This way the service will be already available within the link function - actually within the whole directive.

I reproduced your scenario in this Plunker.

Upvotes: 2

AWolf
AWolf

Reputation: 8980

With angular-scroll you could do it like this:

Pass your scroll target id as state parameter to child state controller and then scroll to the element with scrollToAnimated(element).

If you'd like to have it reusable you could create a factory of that controller and inject it into resolve of child state. That should work too but I haven't tested it.

You probably need to retrigger the scrolling if you're on the child state and click on the state change link again. That's why I've added the method checkState that reloads the current state if we're on child state to repeat the scrolling.

Please have a look at the demo below or in this fiddle.

I think your directive is not working because you're not passing the href attribute to your directive.

You could fix this by passing href="targetId" to your directive where targetId is added with $scope.targetId = $stateParams.id in you child controller. Or you could access the targetId of $scope directly if you're not using an isolated scope.

Anyway I would do it like I did it in the demo or with a factory inside resolve.

angular.module('demoApp', ['ui.router', 'duScroll'])
	.controller('MainControlller', MainController)
	.config(Config);

function MainController($scope, $state) {
	$scope.checkState = function() {
    	//console.log($state.current);
        if($state.current.name == 'parent.child') {
            //console.log('parent.child state');
        	// reload to retrigger scrolling again
           $state.reload();
        }
    };
}

function Config($urlRouterProvider, $stateProvider) {
	$urlRouterProvider.otherwise('/');
    
    $stateProvider
    	.state('parent', {
        	url: '/',
        	templateUrl: 'home.html'
    })
    	.state('parent.child', {
    	url: 'child/:id',
        templateUrl: 'child.html',
        controller: function($scope, $stateParams, $document) {
        	var scrollElement = angular
                .element(document.getElementById($stateParams.id));
            console.log(scrollElement);
            $document.scrollToElementAnimated(scrollElement);
        }
    });
               
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-scroll/0.7.3/angular-scroll.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.15/angular-ui-router.js"></script>
<div ng-app="demoApp" ng-controller="MainController">
    <script type="text/ng-template" id="home.html">
        <a ui-sref="parent.child({id: 'practiceParam'})" ng-click="checkState()">go to practice</a>
        <div ui-view=""></div>
    </script>
    <script type="text/ng-template" id="child.html">
        <!--<a href="#practiceParam" du-smooth-scroll="">scroll to practice</a>
        --><p>
            Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. 
        </p><p>
Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. 
        </p><p>
Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi.</p>
<p>
Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. 
        </p>
        <p>
            Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. 
        </p>
        <h1 id="practiceParam">practice</h1>
        <p>
Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. 
        </p><p>
Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi.</p>
<p>
Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. 
        </p>
        <a ui-sref="parent">back</a>
    </script>
    
    <div ui-view=""></div>
</div>

Upvotes: 2

Related Questions