How to implement back button in Infinite Scrolling in AngularJs? - javascript

i'm building an app with angularjs, i have implemented infinite scrolling technique because i have a lot of items to show, so it's a better experience than pagination, but the bad thing that i'm having it's that if the user clicks on item it goes to item page, but when he wants to go back he will be on the top and he should keep scrolling. that's what i want to accomplish if he hits back button he could back to his position where he were !!
Listing Items:
<div class="container" id="infinit-container" ng-controller="ArticlesController">
<div id="fixed" when-scrolled="LoadMore()">
<div ng-repeat="article in Articles">
<article-post article="article"></article-post>
</div>
</div>
</div>
here is when-scrolled directive
app.directive('whenScrolled', function () {
return function (scope, element, attrs) {
$('#fixed').height($(window).height());
var div = element[0];
element.bind('scroll', function () {
if (div.scrollTop + div.offsetHeight >= div.scrollHeight) {
scope.$apply(attrs.whenScrolled);
}
})
}
})
thats the controller
app.controller('ArticlesController', function ($scope, UserService) {
$scope.Articles = [];
var page = 0;
$scope.LoadMore = function () {
UserService.Articles(page)
.success(function (data) {
$scope.Articles.push.apply($scope.Articles, data);
});
page++;
};
$scope.LoadMore();
});

im using page number, save page number somewhere (i prefer sessionStorage) when going to see the item detail , when user is comming back to list, read page number and load the page.
it is the simple code for main page controller (list)
app.controller('ArticlesController', function($scope, UserService) {
$scope.Articles = [];
var page = 0;
$scope.LoadMore = function() {
UserService.Articles(page)
.success(function(data) {
$scope.Articles.push.apply($scope.Articles, data);
});
page++;
}
$scope.LoadArticle = function(articleId) {
sessionStorage.setItem("pageNumber", page);
/// change path to somewhere you want show your article
};
//it will load the page you coming back from
if (sessionStorage["pageNumber"]) {
page = parseInt(sessionStorage["pageNumber"]);
}
LoadMore();
});
because of using sessionStorage , it will deleted if user close the browser or page as expected.

Related

Load data from Firebase using AngularJS immediately

I am trying to make a comments section on my website. I have properly linked all my files and getting data from Firebase is working. But, the comments only load after I either click on something or enter text in the "Add comment" field; it does not show the comments instantly when the page loads. Does anyone know why?
Relevant code: (placed at the top of my controller)
$scope.comments = {};
database.ref('comments').on('value', function(items) {
$scope.comments = items.val();
});
$scope.comment = function() {
database.ref('comments').push($scope.newComment);
$scope.newComment = "";
};
Use $scope.$apply
$scope.comments = {};
database.ref('comments').on('value', function(items) {
$scope.$apply(function () {
$scope.comments = items.val();
});
});
$scope.comment = function() {
database.ref('comments').push($scope.newComment);
$scope.newComment = "";
};

pre-fetch in AngularJS when view is loaded

I have an angularJS application whit infinite scrolling: this means every time I reach the bottom of the page a new ajax call happens.
I simply want to check when the page is fully loaded, every time an ajax call happens. If I'm able to check if the page is loaded I can pre-fetch the json for next page.
window.onload works only for static pages, and $scope.on/watch('$viewContentLoaded', function() {}) is fired as the first thing when I do an ajax call. I mean: it is fired and after that I can see the items of the ajax call. It should be fired as the last thing, when the page is loaded.
$scope.nextPage = function() {
$http.jsonp(url).success(function(response) {
console.log(response.data);
}
$scope.$watch('$viewContentLoaded', function() {
console.log("page is loaded");
});
}
Ok guys, thanks for help. I've developed the solution and it's working fine.
As usual, for me, AngularJS doc is very clear: it does says nothing, or it's a messy.
I've used ngInfiniteScroll plugin combined with the relative Reddit demo
Just a question: what do you think about how I've used $q? It's not nice for me. I mean I defined interval just to use $q.
HTML
<div ng-app='myApp' ng-controller='DemoController'>
<div infinite-scroll='nextPage()' infinite-scroll-disabled='busy' infinite-scroll-distance='1'>
<div ng-repeat='item in items'>
<span class='score'>{{item.score}}</span>
<span class='title'>
<a ng-href='{{item.url}}' target='_blank'>{{item.title}}</a>
</span>
<small>by {{item.author}} -
<a ng-href='http://reddit.com{{item.permalink}}' target='_blank'>{{item.num_comments}} comments</a>
</small>
<div style='clear: both;'></div>
</div>
<div ng-show='reddit.busy'>Loading ...</div>
</div>
</div>
JS
var myApp = angular.module('myApp', ['infinite-scroll']);
myApp.service('ajaxcall', function($http) {
this.getjson = function (url) {
return $http.jsonp(url).success(function(response) {
console.log('inside service ' + response.data.children);
return response;
});
}
});
myApp.controller('DemoController', function(ajaxcall, $scope, $q) {
$scope.busy = false;
$scope.items = [];
var after = '';
var prefetch = false;
var get_items;
$scope.nextPage = function() {
if ($scope.busy) return;
$scope.busy = true;
if (!prefetch) {
prefetch = true;
get_items = ajaxcall.getjson("https://api.reddit.com/hot?after=" + after + "&jsonp=JSON_CALLBACK");
}
interval = $q.when(get_items).then(function(data) {
if (get_items) {
console.log(data);
var new_items = data.data.data.children;
for (var i = 0; i < new_items.length; i++) {
$scope.items.push(new_items[i].data);
}
after = "t3_" + $scope.items[$scope.items.length - 1].id;
get_items = ajaxcall.getjson("https://api.reddit.com/hot?after=" + after + "&jsonp=JSON_CALLBACK");
$scope.busy = false;
}
})
};
});

How to handle different content depending on selected element in the same sidebar

Imaging that we have animals table. Each row describes one animal, for example: ID, NAME, TYPE.
Depending on the selected row type, I want show is the sidebar content related to that animal and some user actions.
Content is completely different, it pulls from data from different APIs.
But the sidebar placed always in the same position, same size and styles.
Maybe I'll have common actions for each controller, like -> close sidebar.
If sidebar already opened and user switch to another one, sidebar should change immediately.
How should I design such login with angular ?
I got an idea to define one directive in html for sidebar. And set listener for selected row, after that compile dynamically sidebar directive for selected row, and insert into parent (main) sidebar.
Probably also I need to handle destroy of previous one.
I appreciate if anyone can tell is I'm going the right way... or should I change something ?
function dtSidebarDirective($compile, $mdUtil, $mdSidenav, $log, mxRegistry) {
return {
restrict: 'E',
templateUrl: 'app/components/sidebar/sidebar.html',
controller: function($scope) {
// used to replace sidebar data on the fly without recompile
$scope.refresh = function() { }
$scope.close = function(ev) {
$mdSidenav('right').close()
}
},
scope: true,
link: link
};
function link(scope, element) {
// used to detect switching between the same type of elements
var _activeType;
var _childDirective;
var _childScope;
var _childElement;
var _toggle = $mdUtil.debounce(function() {
$mdSidenav('right')
.toggle()
.then(function() {
scope.isOpen = $mdSidenav('right').isOpen();
$log.debug('toggle right is done');
});
});
var _init = function(type, data) {
// by default open diagram sidebar
switch(type) {
case 'shape':
_childDirective = $compile('<dt-dog-sidebar></dt-dog-sidebar>');
break;
case 'text':
_childDirective = $compile('<dt-cat-sidebar></dt-cat-sidebar>');
break;
default:
_childDirective = $compile('<dt-animal-sidebar></dt-diagram-sidebar>');
}
// initialize child sidebar : element & scope
_activeType = type;
_childScope = scope.$new();
_childScope.data = data;
_childElement = _childDirective(_childScope);
element.find('md-sidenav').append(_childElement);
};
var _isInitialized = function(type) {
var isDefined = angular.isDefined(_childDirective);
return type ? _activeType == type && isDefined : isDefined;
};
var _destroy = function() {
if(_isInitialized()) {
_childScope.$destroy();
_childElement.empty();
_childElement.remove();
}
};
function showSidebar(ev, type, data) {
// lets figure out does we open the same kind of sidebar
if(_isInitialized(type)) {
_childScope.data = data;
_childScope.refresh();
return;
}
// destroy since we gonna replace with new sidebar
_destroy();
_init(type, data);
}
function toggle() {
update();
_toggle();
}
function update(ev) {
// detect which sidebar should be shown now
}
scope.$on('sidebar:toggle', toggle);
scope.$on('sidebar:show', showSidebar);
scope.$on('sidebar:update', update);
}
I manage to get it work with recompiling each time I need to should different sidebar or call refresh of children scope.

AngularJS - Change data in ngRepeat after load

I have a bunch of radio groups and a textarea elements that load in a ng-repeat controller. I want to populate all/some of those controls after the controller has finished loading them. I have tried $scope.$on. I have looked at directives, but they are really confusing.
I just want to call a function to populate the form (manipulate DOM) after the controller has finished loading. The controller currently loads (and loads a sub-controller) on the click of a button.
Here's some example code of what I have. The Risks function is what I am calling when the button is clicked.
myApp.controller('spRisks', function ($scope, $http, Service) {
$scope.getRisks = function(){
var caml = "<View><Query><Where><Eq><FieldRef Name='Owner'/><Value Type='Integer'><UserID/></Value></Eq></Where></Query></View>";
var ownerData = getDataWithCaml("Owners", caml);
ownerData.success(function (data) {
var arrayOfExpressions = [];
for (var i = 0; i < data.d.results.length; i++){
arrayOfExpressions.push(CamlBuilder.Expression().LookupMultiField("OwnerTitle").EqualTo(data.d.results[i].Title));
}
var newCaml = new CamlBuilder().View().Query().Where()
.Any(arrayOfExpressions)
.And()
.TextField("Control").IsNotNull()
.ToString();
newCaml = newCaml.replace(/"/g, '\'');
var jsonData = getDataWithCaml("Risks", newCaml);
jsonData.success(function (jsonDataResults) {
$scope.$apply(function(){$scope.Risks = jsonDataResults.d.results;});
});
jQuery('#divQs').show();
});
$scope.$on('$viewContentLoaded', function () {
alert('test');
Service.getAnswers($('#selYear').val(), $('#selMonth').val());
});
}
});

Update angularjs view when using "Controller as var" syntax

I've a small site that has 3 views, and everytime i change the view, new data is loaded into it (Home, search sombody. Dashboard, a collection of data about them, detail, specific deatils about one item).
I am loading the data like such
<section id="recentSearches" class="frame" data-ng-controller="HomeCtrl as ctrl">
<h1>Recently Searched</h1>
<div data-ng-repeat="name in ctrl.recentSearches | reverse">
<a class="name" href="/#/dashboard/{{name | formatSlug}}" data-ng-bind="name"></a>
</div>
</section>
and just for reference, recentSearches is populated in the controller by a service (this is HomeCtrl) like such:
this.recentSearches = Services.recentSearches.data;
My problem is, every time i switch a view, it doesn't update the previous data in the view, when I reload the page though, it assigns the proper data in the view (recalling the data based on the permalink).
I assume this is happening because the data is not in $scope? When i had the data in scope it updated fine, but i am moving away from storing data in scope completely and this is my last hurtle.
Any help is greatly appreciated and let me know if you need any more info or code. Thanks!
UPDATE: One of my controllers, the other 2 are setup in the same way.
'use strict';
angular.module('myApp.Controller.Home', ['ngRoute'])
.config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/', {
templateUrl: 'views/home.html',
pageTitle: 'Home'
});
}])
.controller('HomeCtrl', ['Services', '$rootScope', 'RecentGames', 'Summoners', 'Champions', '$filter',
function(Services, $rootScope, RecentGames, Summoners, Champions, $filter) {
this.slContainer = document.getElementById('summonerLookup');
this.rsContainer = document.getElementById('recentSearches');
this.recentSearches = Services.recentSearches.data;
// Initalize
// -----------------------
this.init = function ( ) {
Services.log('[init][controller] Home');
// Focus on the form
this.slContainer.querySelector('input[name=summonerName]').focus();
// Register Event Listeners
this.events();
// Remove the loading overlay
Services.removeLoading();
};
// Assign Events
// -----------------------
this.events = function ( ) {
// The main form input submit
this.slContainer.addEventListener('submit', this.summonerLookup);
// The recent searches list
this.rsContainer.addEventListener('click', this.loadRecentlySearched.bind(this));
};
// Submit the lookup form
// -----------------------
this.summonerLookup = function ( e, val ) {
if(e){
e.preventDefault();
}
if(!val){
val = $filter('formatSlug')(this.summonerName.value);
}
Summoners.getByName( val ).then(function success(results){ // Grab the summoner
RecentGames.get().then(function success(results){ // Populate the summoners recent games
RecentGames.data = results;
Champions.getUsed();
});
});
};
// Load a recently searched profile
// TODO: load recentsearched from the a tag being clicked, not this.
// -----------------------
this.loadRecentlySearched = function ( e ) {
var el = e.target || e.toElement;
var isButton = el.className.match(/name/g);
// Block event if is not button
if( isButton === null ) { return false; }
// Get summoner information
this.summonerLookup( null, $filter('formatSlug')(el.innerHTML) );
e.preventDefault();
};
return this.init();
}]);

Categories