Sample app: http://angular.github.com/angular-phonecat/step-11/app/#/phones
If you choose the last phone \"Motorola charm\" it will show you the details of the phone
this may solve your problem, it works for me
$httpProvider.defaults.cache = true;
I am using a custom solution in my project.
Step 1: Obtain the position of the click on the list and save it in local storage.
var position = document.body.scrollTop;
localStorage.setItem("scrollPosition",position);
Step 2:In the detail view set a global variable backFromDetailView to true.
backFromDetailView = true;
Step 3:On returning from the detail view page to the list. All the content is reloaded from server again up to the scrolled position.
For this purpose bind a function in the html using the following line:
And the controller contains the function:
$scope.goto = function (){
if(backFromDetailView){
window.scrollTo(0, localStorage.getItem("scrollPosition"));
}
}
Some disadvantages of this technique:
All the content including additional content is reloaded again.
In iOS, a black screen appear before scrolling to the appropriate position.
Unlike the other answers I wanted to remember more than just scrolls, namely input
field value
s.
Not only that, but a lot of them assumed
body
as your scrolling element (e.g. what if you're using angular snap?),ng-view
).<body> <!-- doesn't scroll -->
<div snap-drawers>..</div>
<div snap-content="" history="scrollTop"> <!-- the scrolling div-->
<header>...</header>
<div ng-view>
<input name="email" history="val"> <!-- tag with value we want remembered -->
<div history="scrollLeft" history-watch="scroll" id="evenHorizontalScroll"><!--
custom value we want remembered.
NB it must have an id to be identified after angular
removes it from the DOM between views,
and since it is not a recognised default we can tell my
directive the jquery event function what to watch
--></div>
</div>
</div>
</body>
I've written a[n unfortunately much longer] shared-scope directive that takes care of these problems.
.directive('history', function($compile, $rootScope, $location) {
return {
restrict : 'A',
replace : false,
scope : false,
controller : function($scope, $timeout) {
//holds all the visited views
var states = new Object();
//the current view
var state = null;
//how many names have been generated where the element itself was used
var generated = 0;
//logs events if allowed
function debug(from) {
//comment this to watch it working
//return;
console.log('StateHistory: ' + from);
if (from == 'went')
console.log(state);
}
//applies the remembered state
function apply() {
var element;
//for each item remembered in the state
for (var query in state) {
//use the element directly, otherwise search for it
(state[query].element || $(query))
//use the appropriate function
[state[query].property](
//and set the value
state[query].value
)
;
debug('applying:' + query + ':' + state[query].value);
}
//start recording what the user does from this point onward
$scope.ignore = false;
}
//generates a reference we can use as a map key
$scope.generateRef = function() {
return '' + (++generated);
};
//views changed
$scope.went = function() {
debug('went');
//set the current state
state = states[$location.path()];
//if we dont remember the state of the page for this view
if (!state)
//get recording!
state = states[$location.path()] = new Object();
//apply the state after other directives
//(like anchorScroll + autoscroll) have done their thing
$timeout(apply);
};
//one of the elements we're watching has changed
$scope.changed = function(name, element, property, useObject) {
//if we're not meant to be watching right now
//i.e. if the user isnt the one changing it
if ($scope.ignore) {
debug('ignored');
return;
}
//if we havent recorded anything for this here yet
if (!state[name]) {
//start recording
state[name] = {property:property};
//and remember to leave behind a reference if the name isn't
//good enough (was generated)
if (useObject)
state[name].element = element;
}
//use the requested function to pull the value
state[name].value = element[property]();
debug('changed:' + name + ':' + state[name].value);
};
//initial view
$scope.went();
//subsequent views
$rootScope.$on('$routeChangeSuccess', $scope.went);
$rootScope.$on('$routeChangeError', $scope.went);
$rootScope.$on('$routeChangeStart', function() {
debug('ignoring');
$scope.ignore = true;
});
},
link: function (scope, element, attrs) {
//jquery event function name
var watch = attrs.historyWatch;
//if not set, use these defaults
if (!watch) {
switch (attrs.history) {
case 'val':
watch = 'change';
break;
case 'scrollTop':
watch = 'scroll';
break;
default:
watch = attrs.history;
}
}
//the css selector to re-find the element on view change
var query = null;
//the reference to the state remembered
var name;
//try using the id
if (attrs.id)
name = query = '#' + attrs.id;
//try using the form name
else if (attrs.name)
name = query = '[name=' + attrs.name + ']';
//otherwise we'll need to just reference the element directly
//NB should only be used for elements not swapped out by angular on view change,
//ie nothing within the view. Eg the view itself, to remember scrolling?
else
name = scope.generateRef();
//jquery value function name
var property = attrs.history;
//watch this element from here on out
element.on(watch, function() {
scope.changed(name, element, property, !query);
});
}
};
})
i created a directive that works on the window scroll ( it could updated to work on any element though )
html usage
<div ng-keep-scroll="service.scrollY">
<!-- list of scrolling things here -->
</div>
where "service.scrollY" MUST be a variable within a service. Services retain their state and values, controllers are recreated every time they load and clear their values so you cant use them to store persistent data. the controller has a scope variable pointing to the service.
directive js
app.directive('ngKeepScroll', function ($timeout) {
return function (scope, element, attrs) {
//load scroll position after everything has rendered
$timeout(function () {
var scrollY = parseInt(scope.$eval(attrs.ngKeepScroll));
$(window).scrollTop(scrollY ? scrollY : 0);
}, 0);
//save scroll position on change
scope.$on("$routeChangeStart", function () {
scope.$eval(attrs.ngKeepScroll + " = " + $(window).scrollTop());
});
}
});
I have used the solution of @Joseph Oster in order to create a directive. I have also taken the liberty to update the answer to use:
as the other events are obsolete.
Fiddle is here: http://jsfiddle.net/empie/p5pn3rvL/
Directive source:
angular.module('myapp', ['ngRoute'])
.directive('autoScroll', function ($document, $timeout, $location) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
scope.okSaveScroll = true;
scope.scrollPos = {};
$document.bind('scroll', function () {
if (scope.okSaveScroll) {
scope.scrollPos[$location.path()] = $(window).scrollTop();
}
});
scope.scrollClear = function (path) {
scope.scrollPos[path] = 0;
};
scope.$on('$locationChangeSuccess', function (route) {
$timeout(function () {
$(window).scrollTop(scope.scrollPos[$location.path()] ? scope.scrollPos[$location.path()] : 0);
scope.okSaveScroll = true;
}, 0);
});
scope.$on('$locationChangeStart', function (event) {
scope.okSaveScroll = false;
});
}
};
})
Based on the great answer from br2000, I updated the directive code to work with ui-router. For states with same name but different params I serialize the $state.params object to make up a unique key in the scrollPosCache
object.
.directive("keepScrollPos", function($state, $window, $timeout, $location, $anchorScroll) {
// cache scroll position of each route's templateUrl
var scrollPosCache = {};
// compile function
return function(scope, element, attrs) {
scope.$on('$stateChangeStart', function() {
// store scroll position for the current view
if ($state.current.name) {
scrollPosCache[$state.current.name + JSON.stringify($state.params)] = [ $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.name + JSON.stringify($state.params)] || [ 0, 0 ];
$timeout(function() {
$window.scrollTo(prevScrollPos[0], prevScrollPos[1]);
}, 0);
}
});
}
})