Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to 'remember' scroll position in angularJS w/ Ui.ROUTER

I have a project I'm using angularJS with ui.router and we have a list of events that can be really long (has infinite scroll) if a user clicks on an event to view details a back button is displayed however upon clicking back the scroll of the div resets back to the top! Looking for some suggestions as to if there is anything built in that I might leverage to remember this scroll position, I know theres the anchorscroll service but I am wondering if there isn't something more suited to stop angular from reseting the scroll positions on navigation?? as there are a few similar lists to this that need to remember their state when scrolled.. I've looked into and tried to implement ui-router-extras dsr and sticky but neither are working..
example at http://codedef.com/hapzis_poc/ . not a full proof but should be able to scroll down the events , click about and back and stay in same scroll position..

like image 436
DavidMIRV Avatar asked Mar 03 '15 09:03

DavidMIRV


2 Answers

There is a conversation on the similar topic (ng-view) and the answer given by @br2000 worked for me.

https://stackoverflow.com/a/25073496/3959662

To make his directive work for ui-router do the following:

1. Create new directive like this:

(function () {
    'use strict';

    angular
        .module('your.module.directives')
        .directive('keepScrollPos', keepScrollPos);

    function keepScrollPos($route, $window, $timeout, $location, $anchorScroll, $state) {

        // cache scroll position of each route's templateUrl
        var scrollPosCache = {};

        // compile function
        var directive = function (scope, element, attrs) {

            scope.$on('$stateChangeStart', function () {
                // store scroll position for the current view
                if($state.$current)
                { 
                    scrollPosCache[$state.current.templateUrl] = [$window.pageXOffset, $window.pageYOffset];
                }
             });

            scope.$on('$stateChangeSuccess', function () {
                // if hash is specified explicitly, it trumps previously stored scroll position
                if ($location.hash()) {
                    $anchorScroll();

                // else get previous scroll position; if none, scroll to the top of the page
                } else {
                    var prevScrollPos = scrollPosCache[$state.current.templateUrl] || [0, 0];
                    $timeout(function () {
                        $window.scrollTo(prevScrollPos[0], prevScrollPos[1]);
                }, 0);
            }
        });
    };

    return directive;
}
})();

2. Use it on the element you have the ui-view attribute, in my case:

<div class="col-xs-12" ui-view keep-scroll-pos></div>
like image 182
Marcin Rapacz Avatar answered Nov 11 '22 08:11

Marcin Rapacz


For sticky states to work, you must not destroy the DOM element. Looking at your state structure, you are using the named ui-view body for state home, as per the ui-router-extras documentation. However, you then clobber that ui-view element when transitioning to about state, which also uses the body ui-view. This can be observed by inspecting the DOM.

.state('home', {
        url: "",
        deepStateRedirect: true,
        sticky: true,
        views: {
            "body": {
                templateUrl: 'templates/home.html'
            },
            "event_list@home": {
                sticky: true,
                templateUrl: "templates/eventList.html",
                controller: "eventListCtrl"
            }
        }
    })
    .state('about', {
        url: 'about',
        views: {
            "body": { // BAD, you just clobbered the sticky ui-view with this template
                templateUrl: 'templates/about.html'
            }
        }
    });

Make sure the sticky state's ui-view is not re-used. For your example, put about state in the unnamed view.

    .state('about', {
        url: 'about',
        templateUrl: 'templates/about.html'
    });

index.html:

<div ui-view="body" ng-show="$state.includes('home')"></div>
<div ui-view></div>

js:

app.run(function($rootScope, $state) { $rootScope.$state = $state } );
like image 34
Chris T Avatar answered Nov 11 '22 08:11

Chris T