Rerender list basing on limitTo angularjs - javascript

So I have list outputted by ng-repeat directive and I have filter | limitTo: amount on it.
amount variable is changing depending on user screen size. It is changing correctly but list doesn't re-render when variable is updated.
How can I force it to re-render after amount viariable change?
HTML
<li ng-repeat="n in allCategory | limitTo : amount" order="{{$index+1}}">
JS(controller)
var screenWidth;
var trackListNumber = function () {
screenWidth = $window.innerWidth;
if(screenWidth < 1599) {
console.log(screenWidth);
$scope.amount = 18;
} else {
$scope.amount = 27;
}
};
trackListNumber();
angular.element($window).bind('resize', function () {
trackListNumber();
});
EDIT
I changed code to
var screenWidth;
var trackIconsNumber = $scope.$apply(function() {
screenWidth = $window.innerWidth;
if (screenWidth < 1599) {
console.log(screenWidth);
$scope.iconsAmount = 18;
} else {
$scope.iconsAmount = 9;
}
});
trackIconsNumber();
angular.element($window).bind('resize', function () {
trackIconsNumber();
});
which cause following error:
angular.js:14642 Error: [$rootScope:inprog] $digest already in progress

As explosion-pills said in the comments, you should use $scope.$apply. The implementation should be like so:
Long Version
angular.element($window).bind('resize', function() {
$scope.$apply(function() {
trackListNumber();
});
});
Short Version
angular.element($window).bind('resize', function() {
$scope.$apply(trackListNumber());
});

Related

Document Click Event Stopped on Filter

I have an Event that is called when the document loads to make it able to click each element with the Class 'header' to expand and show more details:
$(document).ready(function () {
InitClick();
});
Here is the function:
function InitClick() {
$('.header').click(function () {
$(this).nextUntil('tr.header').slideToggle();
});
}
Now the issue is when the results get filtered, it will then cause that click event to stop until I re-initialise it, then it will start working again... Until the Data is filtered again or has to update.
Now my main question is, has anyone got a link to something that could assist me, I've tried putting is on a $watch but the main issue I keep having is that this is called before the filtered data is returned, causing it to initialise before the data is there.
Here is MyApp.js if it helps?
var MyApp = angular.module('MyApp', []);
MyApp.controller('MyAppCtrl', function ($scope, $filter) {
$scope.Users = tJSONData;
$scope.currentPage = 0;
$scope.pageSize = 3;
$scope.Pages = [];
$scope.search = '';
$scope.Completed = '';
$scope.getData = function () {
return $filter('filter')($scope.Users, $scope.search || $scope.Completed);
}
$scope.numberOfPages = function () {
return Math.ceil($scope.getData().length / $scope.pageSize);
}
$scope.$watch('numberOfPages()', function (newVal, oldVal) {
var tPages = [];
for (i = 0; i < $scope.numberOfPages(); i++) {
tPages.push(i + 1);
}
if ($scope.currentPage >= $scope.numberOfPages()) {
$scope.currentPage = $scope.numberOfPages() - 1;
}
if ($scope.currentPage == -1) {
$scope.currentPage = 0;
}
return $scope.Pages = tPages;
})
$scope.updatePage = function () {
$scope.$apply(function () {
$scope.Users = tJSONData
})
}
$scope.limitPagination = function () {
if ($scope.currentPage == 0) {
return $scope.currentPage;
} else if ($scope.currentPage == 1) {
return $scope.currentPage - 1
} else {
return $scope.currentPage - 2;
}
}
});
MyApp.filter('startFrom', function () {
return function (input, start) {
start =+ start; //parse to int
return input.slice(start);
}
});
Any help is appreciated :)
In a nutshell, when you call $(selector).click (or any other event), it will only set up the event for the elements that were grabbed at the time of the call. If the elements are removed and replaced (as seems to be your case), the new elements won't automatically have it.
Instead of using $(selector).click(callback), use $(document).on('click', selector, callback)
In your case, it looks like this should do the trick:
$(document).on('click', '.header', function () {
$(this).nextUntil('tr.header').slideToggle();
});
The difference is that this way, the event is actually bound to the document itself, which never changes. You then filter so it'll only trigger when you click a .header, but it'll apply to all of them, even those added after this bit is called.

How do I disable an element after it's clicked in Angular

I have 2 elements that are bound to a click function inside a directive using Angular.
The problem I'm having is when one of those elements are are clicked very quickly, the other element would fire.
You can see an example here: http://430designs.com/xperience/black-label-app/deck.php
If you click the "X" several times, rapidly, you'll see the heart glow instead of the "X". You may have to do it a few times to actually see it happen, but it will happen.
I need to disable the heart/dislike buttons after the click and then reenable them when the function is finished.
Here's my controller code. The directive for the "fake swipe" starts on line 87:
angular.module('black-label', ['ngTouch', 'ngSwippy'])
.controller('MainController', function($scope, $timeout, $window) {
$scope.cardsCollection = [{thumbnail:'images/deck/thor_01.jpg',collection:'thoroughbred',},{thumbnail:'images/deck/thor_02.jpg',collection:'thoroughbred',},{thumbnail:'images/deck/thor_03.jpg',collection:'thoroughbred',},{thumbnail:'images/deck/thor_04.jpg',collection:'thoroughbred',},{thumbnail:'images/deck/thor_05.jpg',collection:'thoroughbred',},{thumbnail:'images/deck/thor_06.jpg',collection:'thoroughbred',},{thumbnail:'images/deck/rhap_01.jpg',collection:'rhapsody',},{thumbnail:'images/deck/rhap_02.jpg',collection:'rhapsody',},{thumbnail:'images/deck/rhap_03.jpg',collection:'rhapsody',},{thumbnail:'images/deck/rhap_04.jpg',collection:'rhapsody',},{thumbnail:'images/deck/rhap_05.jpg',collection:'rhapsody',},{thumbnail:'images/deck/rhap_06.jpg',collection:'rhapsody',},{thumbnail:'images/deck/cha_01.jpg',collection:'chalet',},{thumbnail:'images/deck/cha_02.jpg',collection:'chalet',},{thumbnail:'images/deck/cha_03.jpg',collection:'chalet',},{thumbnail:'images/deck/cha_04.jpg',collection:'chalet',},{thumbnail:'images/deck/cha_05.jpg',collection:'chalet',},{thumbnail:'images/deck/cha_06.jpg',collection:'chalet',},{thumbnail:'images/deck/mod_01.jpg',collection:'modern',},{thumbnail:'images/deck/mod_02.jpg',collection:'modern',},{thumbnail:'images/deck/mod_03.jpg',collection:'modern',},{thumbnail:'images/deck/mod_04.jpg',collection:'modern',},{thumbnail:'images/deck/mod_05.jpg',collection:'modern',},{thumbnail:'images/deck/mod_06.jpg',collection:'modern',},{thumbnail:'images/deck/ind_01.jpg',collection:'indulgence',},{thumbnail:'images/deck/ind_02.jpg',collection:'indulgence',},{thumbnail:'images/deck/ind_03.jpg',collection:'indulgence',},{thumbnail:'images/deck/ind_04.jpg',collection:'indulgence',},{thumbnail:'images/deck/ind_05.jpg',collection:'indulgence',},{thumbnail:'images/deck/ind_06.jpg',collection:'indulgence',},{thumbnail:'images/deck/cnt_01.jpg',collection:'center-stage',},{thumbnail:'images/deck/cnt_02.jpg',collection:'center-stage',},{thumbnail:'images/deck/cnt_03.jpg',collection:'center-stage',},{thumbnail:'images/deck/cnt_04.jpg',collection:'center-stage',},{thumbnail:'images/deck/cnt_05.jpg',collection:'center-stage',},{thumbnail:'images/deck/cnt_06.jpg',collection:'center-stage',},{thumbnail:'images/deck/vin_01.jpg',collection:'vineyard',},{thumbnail:'images/deck/vin_02.jpg',collection:'vineyard',},{thumbnail:'images/deck/vin_03.jpg',collection:'vineyard',},{thumbnail:'images/deck/vin_04.jpg',collection:'vineyard',},{thumbnail:'images/deck/vin_05.jpg',collection:'vineyard',},{thumbnail:'images/deck/vin_06.jpg',collection:'vineyard',}];
// Do the shuffle
var shuffleArray = function(array) {
var m = array.length,
t, i;
// While there remain elements to shuffle
while (m) {
// Pick a remaining element
i = Math.floor(Math.random() * m--);
// And swap it with the current element.
t = array[m];
array[m] = array[i];
array[i] = t;
}
return array;
};
$scope.deck = shuffleArray($scope.cardsCollection);
$scope.myCustomFunction = function() {
$timeout(function() {
$scope.clickedTimes = $scope.clickedTimes + 1;
$scope.actions.unshift({ name: 'Click on item' });
});
}; //end myCustomFunction
$scope.count = 0;
$scope.showinfo = false;
$scope.clickedTimes = 0;
$scope.actions = [];
$scope.picks = [];
var counterRight = 0;
var counterLeft = 0;
var newVar = $scope;
$scope.swipeend = function() {
$scope.actions.unshift({ name: 'Collection Empty' });
$window.location.href = 'theme-default.php';
}; //endswipeend
$scope.swipeLeft = function(person) {
//Essentially do nothing
$scope.actions.unshift({ name: 'Left swipe' });
$('.circle.x').addClass('dislike');
$('.circle.x').removeClass('dislike');
$(this).each(function() {
return counterLeft++;
});
}; //end swipeLeft
$scope.swipeRight = function(person) {
$scope.actions.unshift({ name: 'Right swipe' });
// Count the number of right swipes
$(this).each(function() {
return counterRight++;
});
$scope.picks.push(person.collection);
// Checking the circles
$('.circle').each(function() {
if (!$(this).hasClass('checked')) {
$(this).addClass('checked');
return false;
}
});
if (counterRight === 4) {
// Calculate and store the frequency of each swipe
var frequency = $scope.picks.reduce(function(frequency, swipe) {
var sofar = frequency[swipe];
if (!sofar) {
frequency[swipe] = 1;
} else {
frequency[swipe] = frequency[swipe] + 1;
}
return frequency;
}, {});
Object.keys(frequency).forEach(function(element) {
console.log('Person ', element,': ', frequency[element]);
});
var max = Math.max.apply(null, Object.keys(frequency).map(function(k){ return frequency[k]; })); // most frequent
// find key for the most frequent value
var winner = Object.keys(frequency).find(function(element){return frequency[element] == max; });
//Underscore
// var winner = _.findKey(frequency, val => val === max);
$window.location.href = 'theme-' + winner + '.php';
} //end 4 swipes
}; //end swipeRight
})
.directive('ngSwippy', ['swipe', function(swipe) {
return {
restrict: 'E',
link: function (scope, element, attrs, controller) {
$(".fake-swipe").on("click", function(evt) {
var sign = $(this).hasClass("swippy-like")?1:-1;
var card = $("div.content-wrapper.swipable-card:last", element/*"div.ng-swippy"*/);
$(this).addClass('happy');
setTimeout(function() {
card.trigger("mousemove");
},300);
$(this).removeClass('happy');
card.trigger("mousedown");
card.animate({ left:sign*$("body").width() }, 350, function() {
card.trigger("mouseup");
});
evt.preventDefault();
return false;
});
}
};
}]);
Similar to #LodewijkBogaards solution, you can also have a variable in the controller, which is true while the click listener is running. To implement such a behavior you simply add a variable to the controller (e.g. var isRunning = false;). You then need to add a condition to the click listener function:
var isRunning = false;
function click() {
if(!isRunning) {
isRunning = true;
[...your code...]
isRunning = false;
}
}
This will also work on elements like a <div/> or an <a/>, whereas the solution of adding ng-disabled will only work on buttons.
The general principle is this: The first thing you do when you handle the click event is set some boolean to true, e.g. $scope.actionInProgress = true. On your button you should have the attribute ng-disabled="!actionInProgress". Then when you action completes you simply set $scope.actionInProgress = false.
People answering questions on StackOverflow generally don't like doing your work in your codebase. People like answering questions that are reduced to the heart of the problem. I am also one of those people and I am quite certain from the looks of your code you are quite capable of making such a change slight change yourself.
Good luck!

Postion().top is not consistant due angular DOM still loading

Position top is not consistant every time ,
If user scrolls down when dom is still loading position will be different.
angular.module('users').directive('appplyProperty', ['$window', '$timeout',
function ($window, $timeout) {
var $win = angular.element($window);
return {
restrict: 'A',
link: function (scope, element, attrs) {
var = offsetTop = element.offset().top;
$win.on('scroll', function (e) {
var checkTheTop = $window.scrollY - offsetTop;
if (checkTheTop > 0) {
// apply css property top = checkTheTop ;
} else {
//do something
}
});
});
}
}]);
How to make sure position is calculated only after DOM is loaded or condition that now nothing is happening in the dom calculate position ?
Use ready method to ensure that your angular DOM is ready.
angular.element(document).ready(function () {
$win.on('scroll', function (e) {
var checkTheTop = $window.scrollY - offsetTop;
if (checkTheTop > 0) {
// apply css property top = checkTheTop ;
} else {
//do something
}
});
});

infinite scroll in angular directive

I coded the below directive for infinite scroll, my problem which I couldn't figure out why it just fire once when the directive is loaded, I need your advice on how to make my list infinite-scroll.
I'm using it to get data remotely and each time i'm calling it I add to the counter 25, so each time it would return more data.
Thanx,
angular.module('MyApp')
.controller('InboxCtrl', function($scope, InboxFactory) {
var counter = 0;
$scope.loadData = function() {
var promise = InboxFactory.getEvents(counter);
promise.then(function(result) {
$scope.events = result;
});
counter += 25;
};
});
angular.module('MyApp')
.factory('InboxFactory', function($http, $q) {
// Service logic
var defered = $q.defer();
function getUrl(count) {
return "api/inbox/get?request={'what':'Search','criteria':'inbox','criteriaId':null,'startTime':null,'endTime':null,'offset':" + count + ",'limit':25,'order':'event_time','direction':'DESC','source':''}";
}
function extract(result) {
return result.data.data;
}
// Public API here
return {
getEvents: function(count) {
$http.get(getUrl(count)).then(
function(result) {
defered.resolve(extract(result))
}, function(err) {
defered.reject(err);
}
);
return defered.promise;
}
};
});
angular.module('MyApp')
.directive('infiniteScroll', ['$timeout',
function(timeout) {
return {
link: function(scope, element, attr) {
var
lengthThreshold = attr.scrollThreshold || 50,
timeThreshold = attr.timeThreshold || 400,
handler = scope.$eval(attr.infiniteScroll),
promise = null,
lastRemaining = 9999;
lengthThreshold = parseInt(lengthThreshold, 10);
timeThreshold = parseInt(timeThreshold, 10);
if (!handler || !components.isFunction(handler)) {
handler = components.noop;
}
element.bind('scroll', function() {
var
remaining = element[0].scrollHeight - (element[0].clientHeight + element[0].scrollTop);
//if we have reached the threshold and we scroll down
if (remaining < lengthThreshold && (remaining - lastRemaining) < 0) {
//if there is already a timer running which has no expired yet we have to cancel it and restart the timer
if (promise !== null) {
timeout.cancel(promise);
}
promise = timeout(function() {
handler();
promise = null;
}, timeThreshold);
}
lastRemaining = remaining;
});
}
};
}
]);
<ul class="inbox-list" infinite-scroll="loadData()">
<li class="clearfix" ng-repeat="event in events">{{event}}</li>
</ul>
I Made some changes the more important is the use of ng-transclude and the creation of a new scope for the directive to pass the method and the parameters. You can have a look at the jsbind. Of course the data are hard coded so i could fake the behaviour.
<ul class="inbox-list" my-infinite-scroll composite-method="loadData()">

Setting an Angular ng-repeat limit

I am trying to setup a limit on my slides, as it stands i have added data-ng-repeat="job in jobs | limitTo : 10"i noticing 12 slides are still displaying. Is there something incorrect my code for this function not to work Plunkr
var timer;
$scope.startAuto = function() {
timer = $interval(function(){
$scope.jobNotification = ($scope.jobNotification + 1) % $scope.jobs.length;
}, 5000);
};
$scope.isActive = function (index) {
return $scope.jobNotification === index;
};
$scope.showJobNotification = function (index) {
if (timer){
$interval.cancel(timer);
$scope.startAuto();
}
$scope.jobNotification = index;
};
$scope.stopAuto = function() {
console.log('tickCtrl.stopAuto() triggered');
if(timer) {
$interval.cancel(timer);
timer = undefined;
}
}
You need to limit the jobNotification value. Reducing the modulo to 10 prevents slide 11 and 12 from ever showing.
$scope.jobNotification = ($scope.jobNotification + 1) % 10;

Categories