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());
});
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!
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
}
});
});
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()">
When using ng-click on a div:
<div ng-click="doSomething()">bla bla</div>
ng-click fires even if the user only selects or drags the div. How do I prevent that, while still enabling text selection?
In requiring something similar, where the usual text selection behavior is required on an element which should otherwise respond to ngClick, I wrote the following directive, which may be of use:
.directive('selectableText', function($window, $timeout) {
var i = 0;
return {
restrict: 'A',
priority: 1,
compile: function (tElem, tAttrs) {
var fn = '$$clickOnNoSelect' + i++,
_ngClick = tAttrs.ngClick;
tAttrs.ngClick = fn + '($event)';
return function(scope) {
var lastAnchorOffset, lastFocusOffset, timer;
scope[fn] = function(event) {
var selection = $window.getSelection(),
anchorOffset = selection.anchorOffset,
focusOffset = selection.focusOffset;
if(focusOffset - anchorOffset !== 0) {
if(!(lastAnchorOffset === anchorOffset && lastFocusOffset === focusOffset)) {
lastAnchorOffset = anchorOffset;
lastFocusOffset = focusOffset;
if(timer) {
$timeout.cancel(timer);
timer = null;
}
return;
}
}
lastAnchorOffset = null;
lastFocusOffset = null;
// delay invoking click so as to watch for user double-clicking
// to select words
timer = $timeout(function() {
scope.$eval(_ngClick, {$event: event});
timer = null;
}, 250);
};
};
}
};
});
Plunker: http://plnkr.co/edit/kkfXfiLvGXqNYs3N6fTz?p=preview
I had to deal with this, too, and came up with something much simpler. Basically you store the x-position on mousedown and then compare new x-position on mouseup:
ng-mousedown="setCurrentPos($event)"
ng-mouseup="doStuff($event)"
Function setCurrentPos():
var setCurrentPos = function($event) {
$scope.currentPos = $event.offsetX;
}
Function doStuff():
var doStuff = function ($event) {
if ($event.offsetX == $scope.currentPos) {
// Only do stuff here, when mouse was NOT dragged
} else {
// Do other stuff, which includes mouse dragging
}
}