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!
Related
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.
I want to make a delay inside my for loop, but it won't really work.
I've already tried my ways that are on stackoverflow, but just none of them work for what I want.
This is what I've got right now:
var iframeTimeout;
var _length = $scope.iframes.src.length;
for (var i = 0; i < _length; i++) {
// create a closure to preserve the value of "i"
(function (i) {
$scope.iframeVideo = false;
$scope.iframes.current = $scope.iframes.src[i];
$timeout(function () {
if ((i + 1) == $scope.iframes.src.length) {
$interval.cancel(iframeInterval);
/*Change to the right animation class*/
$rootScope.classess = {
pageClass: 'nextSlide'
}
currentId++;
/*More information about resetLoop at the function itself*/
resetLoop();
} else {
i++;
$scope.iframes.current = $scope.iframes.src[i];
}
}, $scope.iframes.durationValue[i]);
}(i));
}
alert("done");
This is what I want:
First of all I got an object that holds src, duration and durationValue.
I want to play both video's that I have in my object.
I check how many video's I've got
I make iframeVideo visible (ngHide)
I insert the right <iframe> tag into my div container
It starts the $timeout with the right duration value
If that's done, do the same if there is another video. When it was the last video it should fire some code.
I hope it's all clear.
I've also tried this:
var iframeInterval;
var i = 0;
$scope.iframeVideo = false;
$scope.iframes.current = $scope.iframes.src[i];
iframeInterval = $interval(function () {
if ((i + 1) == $scope.iframes.src.length) {
$interval.cancel(iframeInterval);
/*Change to the right animation class*/
$rootScope.classess = {
pageClass: 'nextSlide'
}
currentId++;
/*More information about resetLoop at the function itself*/
resetLoop();
} else {
i++;
$scope.iframes.current = $scope.iframes.src[i];
}
}, $scope.iframes.durationValue[i])
Each $timeout returns a different promise. To properly cancel them, you need to save everyone of them.
This example schedules several subsequent actions starting at time zero.
var vm = $scope;
vm.playList = []
vm.playList.push({name:"video1", duration:1200});
vm.playList.push({name:"video2", duration:1300});
vm.playList.push({name:"video3", duration:1400});
vm.playList.push({name:"video4", duration:1500});
vm.watchingList=[];
var timeoutPromiseList = [];
vm.isPlaying = false;
vm.start = function() {
console.log("start");
//ignore if already playing
if (vm.isPlaying) return;
//otherwise
vm.isPlaying = true;
var time = 0;
for (var i = 0; i < vm.playList.length; i++) {
//IIFE closure
(function (i,time) {
console.log(time);
var item = vm.playList[i];
var p = $timeout(function(){playItem(item)}, time);
//push each promise to list
timeoutPromiseList.push(p);
})(i,time);
time += vm.playList[i].duration;
}
console.log(time);
var lastPromise = $timeout(function(){vm.stop()}, time);
//push last promise
timeoutPromiseList.push(lastPromise);
};
Then to stop, cancel all of the $timeout promises.
vm.stop = function() {
console.log("stop");
for (i=0; i<timeoutPromiseList.length; i++) {
$timeout.cancel(timeoutPromiseList[i]);
}
timeoutPromiseList = [];
vm.isPlaying = false;
};
The DEMO on PLNKR.
$timeout returns promise. You can built a recursive chain of promises like this, so every next video will play after a small amount of time.
I edited the TypeAhead directive that is part of Angular UI for AngularJS so it will only give suggestions based on the most recent word, delimited by space (" ").
I intend to use it to for something like a query builder, dynamically giving suggestions based on surrounding syntax. This works as expected for the first word, but once we get to the second word, the promise does not resolve anymore for some reason. The value of inputValue is correct and as expected, but the code inside
$q.when(parserResult.source(originalScope, locals)).then(function (matches) {
does not appear to get run. Please advise.
My code (exactly the same as original except for I added a function called getLastWord that truncates the current expression :
angular.module('customTypeahead', ['ui.bootstrap.position', 'ui.bootstrap.bindHtml'])
.factory('customTypeaheadParser', ['$parse', function ($parse) {
// 00000111000000000000022200000000000000003333333333333330000000000044000
var TYPEAHEAD_REGEXP = /^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w\d]*))\s+in\s+([\s\S]+?)$/;
return {
parse: function (input) {
var match = input.match(TYPEAHEAD_REGEXP);
if (!match) {
throw new Error(
'Expected customTypeahead specification in form of "_modelValue_ (as _label_)? for _item_ in _collection_"' +
' but got "' + input + '".');
}
return {
itemName: match[3],
source: $parse(match[4]),
viewMapper: $parse(match[2] || match[1]),
modelMapper: $parse(match[1])
};
}
};
}])
.directive('customTypeahead', ['$compile', '$parse', '$q', '$timeout', '$document', '$rootScope', '$position', 'customTypeaheadParser',
function ($compile, $parse, $q, $timeout, $document, $rootScope, $position, customTypeaheadParser) {
var HOT_KEYS = [9, 13, 27, 38, 40];
return {
require: 'ngModel',
link: function (originalScope, element, attrs, modelCtrl) {
//SUPPORTED ATTRIBUTES (OPTIONS)
//minimal no of characters that needs to be entered before customTypeahead kicks-in
var minLength = originalScope.$eval(attrs.customTypeaheadMinLength);
if (!minLength && minLength !== 0) {
minLength = 0;
}
//minimal wait time after last character typed before customTypeahead kicks-in
var waitTime = originalScope.$eval(attrs.customTypeaheadWaitMs) || 0;
//should it restrict model values to the ones selected from the popup only?
var isEditable = originalScope.$eval(attrs.customTypeaheadEditable) !== false;
//binding to a variable that indicates if matches are being retrieved asynchronously
var isLoadingSetter = $parse(attrs.customTypeaheadLoading).assign || angular.noop;
//a callback executed when a match is selected
var onSelectCallback = $parse(attrs.customTypeaheadOnSelect);
var inputFormatter = attrs.customTypeaheadInputFormatter ? $parse(attrs.customTypeaheadInputFormatter) : undefined;
var appendToBody = attrs.customTypeaheadAppendToBody ? originalScope.$eval(attrs.customTypeaheadAppendToBody) : false;
var focusFirst = originalScope.$eval(attrs.customTypeaheadFocusFirst) !== false;
//INTERNAL VARIABLES
//model setter executed upon match selection
var $setModelValue = $parse(attrs.ngModel).assign;
//expressions used by customTypeahead
var parserResult = customTypeaheadParser.parse(attrs.customTypeahead);
var hasFocus;
//create a child scope for the customTypeahead directive so we are not polluting original scope
//with customTypeahead-specific data (matches, query etc.)
var scope = originalScope.$new();
originalScope.$on('$destroy', function () {
scope.$destroy();
});
// WAI-ARIA
var popupId = 'customTypeahead-' + scope.$id + '-' + Math.floor(Math.random() * 10000);
element.attr({
'aria-autocomplete': 'list',
'aria-expanded': false,
'aria-owns': popupId
});
//pop-up element used to display matches
var popUpEl = angular.element('<div custom-typeahead-popup></div>');
popUpEl.attr({
id: popupId,
matches: 'matches',
active: 'activeIdx',
select: 'select(activeIdx)',
query: 'query',
position: 'position'
});
//custom item template
if (angular.isDefined(attrs.customTypeaheadTemplateUrl)) {
popUpEl.attr('template-url', attrs.customTypeaheadTemplateUrl);
}
var resetMatches = function () {
scope.matches = [];
scope.activeIdx = -1;
element.attr('aria-expanded', false);
};
var getMatchId = function (index) {
return popupId + '-option-' + index;
};
// Indicate that the specified match is the active (pre-selected) item in the list owned by this customTypeahead.
// This attribute is added or removed automatically when the `activeIdx` changes.
scope.$watch('activeIdx', function (index) {
if (index < 0) {
element.removeAttr('aria-activedescendant');
} else {
element.attr('aria-activedescendant', getMatchId(index));
}
});
var getLastWord = function (expression) {
if (expression === "") {
return "";
}
var temp = expression.split(" ");
return temp[temp.length - 1];
};
var getMatchesAsync = function (inputValue) {
inputValue = getLastWord(inputValue);
var locals = {$viewValue: inputValue};
isLoadingSetter(originalScope, true);
$q.when(parserResult.source(originalScope, locals)).then(function (matches) {
//it might happen that several async queries were in progress if a user were typing fast
//but we are interested only in responses that correspond to the current view value
var onCurrentRequest = (inputValue === modelCtrl.$viewValue);
if (onCurrentRequest && hasFocus) {
if (matches && matches.length > 0) {
scope.activeIdx = focusFirst ? 0 : -1;
scope.matches.length = 0;
//transform labels
for (var i = 0; i < matches.length; i++) {
locals[parserResult.itemName] = matches[i];
scope.matches.push({
id: getMatchId(i),
label: parserResult.viewMapper(scope, locals),
model: matches[i]
});
}
scope.query = inputValue;
//position pop-up with matches - we need to re-calculate its position each time we are opening a window
//with matches as a pop-up might be absolute-positioned and position of an input might have changed on a page
//due to other elements being rendered
scope.position = appendToBody ? $position.offset(element) : $position.position(element);
scope.position.top = scope.position.top + element.prop('offsetHeight');
element.attr('aria-expanded', true);
} else {
resetMatches();
}
}
if (onCurrentRequest) {
isLoadingSetter(originalScope, false);
}
}, function () {
resetMatches();
isLoadingSetter(originalScope, false);
});
};
resetMatches();
//we need to propagate user's query so we can highlight matches
scope.query = undefined;
//Declare the timeout promise var outside the function scope so that stacked calls can be cancelled later
var timeoutPromise;
var scheduleSearchWithTimeout = function (inputValue) {
timeoutPromise = $timeout(function () {
getMatchesAsync(inputValue);
}, waitTime);
};
var cancelPreviousTimeout = function () {
if (timeoutPromise) {
$timeout.cancel(timeoutPromise);
}
};
//plug into $parsers pipeline to open a customTypeahead on view changes initiated from DOM
//$parsers kick-in on all the changes coming from the view as well as manually triggered by $setViewValue
modelCtrl.$parsers.unshift(function (inputValue) {
inputValue = getLastWord(inputValue);
hasFocus = true;
if (minLength === 0 || inputValue && inputValue.length >= minLength) {
if (waitTime > 0) {
cancelPreviousTimeout();
scheduleSearchWithTimeout(inputValue);
} else {
getMatchesAsync(inputValue);
}
} else {
isLoadingSetter(originalScope, false);
cancelPreviousTimeout();
resetMatches();
}
if (isEditable) {
return inputValue;
} else {
if (!inputValue) {
// Reset in case user had typed something previously.
modelCtrl.$setValidity('editable', true);
return inputValue;
} else {
modelCtrl.$setValidity('editable', false);
return undefined;
}
}
});
modelCtrl.$formatters.push(function (modelValue) {
var candidateViewValue, emptyViewValue;
var locals = {};
// The validity may be set to false via $parsers (see above) if
// the model is restricted to selected values. If the model
// is set manually it is considered to be valid.
if (!isEditable) {
modelCtrl.$setValidity('editable', true);
}
if (inputFormatter) {
locals.$model = modelValue;
return inputFormatter(originalScope, locals);
} else {
//it might happen that we don't have enough info to properly render input value
//we need to check for this situation and simply return model value if we can't apply custom formatting
locals[parserResult.itemName] = modelValue;
candidateViewValue = parserResult.viewMapper(originalScope, locals);
locals[parserResult.itemName] = undefined;
emptyViewValue = parserResult.viewMapper(originalScope, locals);
return candidateViewValue !== emptyViewValue ? candidateViewValue : modelValue;
}
});
scope.select = function (activeIdx) {
//called from within the $digest() cycle
var locals = {};
var model, item;
locals[parserResult.itemName] = item = scope.matches[activeIdx].model;
model = parserResult.modelMapper(originalScope, locals);
$setModelValue(originalScope, model);
modelCtrl.$setValidity('editable', true);
modelCtrl.$setValidity('parse', true);
onSelectCallback(originalScope, {
$item: item,
$model: model,
$label: parserResult.viewMapper(originalScope, locals)
});
resetMatches();
//return focus to the input element if a match was selected via a mouse click event
// use timeout to avoid $rootScope:inprog error
$timeout(function () {
element[0].focus();
}, 0, false);
};
//bind keyboard events: arrows up(38) / down(40), enter(13) and tab(9), esc(27)
element.bind('keydown', function (evt) {
//customTypeahead is open and an "interesting" key was pressed
if (scope.matches.length === 0 || HOT_KEYS.indexOf(evt.which) === -1) {
return;
}
// if there's nothing selected (i.e. focusFirst) and enter is hit, don't do anything
if (scope.activeIdx == -1 && (evt.which === 13 || evt.which === 9)) {
return;
}
evt.preventDefault();
if (evt.which === 40) {
scope.activeIdx = (scope.activeIdx + 1) % scope.matches.length;
scope.$digest();
} else if (evt.which === 38) {
scope.activeIdx = (scope.activeIdx > 0 ? scope.activeIdx : scope.matches.length) - 1;
scope.$digest();
} else if (evt.which === 13 || evt.which === 9) {
scope.$apply(function () {
scope.select(scope.activeIdx);
});
} else if (evt.which === 27) {
evt.stopPropagation();
resetMatches();
scope.$digest();
}
});
element.bind('blur', function (evt) {
hasFocus = false;
});
// Keep reference to click handler to unbind it.
var dismissClickHandler = function (evt) {
if (element[0] !== evt.target) {
resetMatches();
if (!$rootScope.$$phase) {
scope.$digest();
}
}
};
$document.bind('click', dismissClickHandler);
originalScope.$on('$destroy', function () {
$document.unbind('click', dismissClickHandler);
if (appendToBody) {
$popup.remove();
}
// Prevent jQuery cache memory leak
popUpEl.remove();
});
var $popup = $compile(popUpEl)(scope);
if (appendToBody) {
$document.find('body').append($popup);
} else {
element.after($popup);
}
}
};
}])
.directive('customTypeaheadPopup', function () {
return {
restrict: 'EA',
scope: {
matches: '=',
query: '=',
active: '=',
position: '&',
select: '&'
},
replace: true,
templateUrl: 'html/templates/custom-typeahead-popup.html',
link: function (scope, element, attrs) {
scope.templateUrl = attrs.templateUrl;
scope.isOpen = function () {
return scope.matches.length > 0;
};
scope.isActive = function (matchIdx) {
return scope.active == matchIdx;
};
scope.selectActive = function (matchIdx) {
scope.active = matchIdx;
};
scope.selectMatch = function (activeIdx) {
scope.select({activeIdx: activeIdx});
};
}
};
})
.directive('customTypeaheadMatch', ['$templateRequest', '$compile', '$parse', function ($templateRequest, $compile, $parse) {
return {
restrict: 'EA',
scope: {
index: '=',
match: '=',
query: '='
},
link: function (scope, element, attrs) {
var tplUrl = $parse(attrs.templateUrl)(scope.$parent) || 'html/templates/custom-typeahead-match.html';
$templateRequest(tplUrl).then(function (tplContent) {
$compile(tplContent.trim())(scope, function (clonedElement) {
element.replaceWith(clonedElement);
});
});
}
};
}])
.filter('customTypeaheadHighlight', function () {
function escapeRegexp(queryToEscape) {
return queryToEscape.replace(/([.?*+^$[\]\\(){}|-])/g, '\\$1');
}
return function (matchItem, query) {
return query ? ('' + matchItem).replace(new RegExp(escapeRegexp(query), 'gi'), '<strong>$&</strong>') : matchItem;
};
});
I have a factory function that i want to use in three different controllers, I want to use if factory code block inside if condition of controller and else factory code block i want to use in else condition of controller. How to achieve this task using factory any help will be appreciated.
So far tried code below...
Factory.js
angular.module("App").factory('geoTreeFactory', function() {
return {
getTreeCheck: function (geoLocation) {
if (geoLocation.id === 5657){
$.each(geoLocation.parent(),function(index,location) {
if (location.id !== geoLocation.id) {
$.each(location._childrenOptions.data.items,function(index,child){
var disableChildId = 'disabled' + child.id;
var model = $parse(disableChildId);
model.assign($scope, true);
})
var disableItemId = 'disabled' + location.id;
var model = $parse(disableItemId);
model.assign($scope, true);
}
});
}
// If a child is checked Disable the parents
try {
var parent = geoLocation.parent().parent();
var disableParentId = 'disabled' + parent.id;
var parentModel = $parse(disableParentId);
parentModel.assign($scope, true);
} catch (err) {
// Ignore since this happens when a node is selected which has no children
}
//Expand and collapse tree on parent check so childrens can be disabled.
$scope.riskInPrcsgeoLocationTree.expand($scope.riskInPrcsgeoLocationTree.findByText(geoLocation.text));
$scope.riskInPrcsgeoLocationTree.collapse($scope.riskInPrcsgeoLocationTree.findByText(geoLocation.text));
//If the parent item is checked, disable all the children
if(geoLocation.items) {
$.each(geoLocation.items,function(index,location) {
var disableItemId = 'disabled' + location.id;
var model = $parse(disableItemId);
model.assign($scope, true);
});
}
},
//Else block function if ocnditions are false
getTreeUncheck: function(geoLocation) {
if (geoLocation.id === 5657){
var getParent = geoLocation.parent();
$.each(geoLocation.parent(),function(index,location) {
if (location.id !== geoLocation.id) {
$.each(location._childrenOptions.data.items,function(index,child){
var disableChildId = 'disabled' + child.id;
var model = $parse(disableChildId);
model.assign($scope, false);
})
var disableItemId = 'disabled' + location.id;
var model = $parse(disableItemId);
model.assign($scope, false);
}
});
}
// If child is unchecked Enable the parent
try {
var parent = geoLocation.parent().parent();
var checkedChildrens = [];
for (var i=0; i<selectedRiskGeoLocations.length; i++){
var checkNodes = selectedRiskGeoLocations[i];
checkedChildrens.push(checkNodes);
}
if (checkedChildrens.length === 0){
var disableParentId = 'disabled' + parent.id;
var parentModel = $parse(disableParentId);
parentModel.assign($scope, false);
};
}
catch (err) {
// Ignore since this happens when a node is selected which has no children
}
//If the parent item is unchecked, enable the childrens
if(geoLocation.items){
$.each(geoLocation.items,function(index,location){
var disableItemId = 'disabled' + location.id;
var model = $parse(disableItemId);
model.assign($scope, false);
});
}
}
};
Controller.js
var selectedCtlGeoLocations = [];
var selectedCtlGeoLocationIds = [];
$scope.populateControlInPrcsGeoLoction = function(geoLocation) {
var pos = $.inArray(geoLocation.text,selectedCtlGeoLocations);
if (pos < 0) {
selectedCtlGeoLocations.push(geoLocation.text);
selectedCtlGeoLocationIds.push(geoLocation.id);
// If the parent item is checked, disable all the
// children
geoTreeFactory.getTreeIfBlock();
} else {
selectedCtlGeoLocations.splice(pos, 1);
selectedCtlGeoLocationIds.splice($.inArray(
geoLocation.id, selectedCtlGeoLocationIds),
1);
// If the parent item is unchecked, enable the
// children
geoTreeFactory.getTreeElseBlock();
}
};
Alright, I could be way off here, but is this what you're trying to do?
app.factory('myservice', function(){
return {
ifBlock: function(){
// code to run if condition is true
},
elseBlock: function(){
// code to run if condition is false
}
}
});
app.controller('main', function(myservice){
$scope.myevent = function(geoLocation){
if(condition){
myservice.ifBlock(geoLocation);
} else {
myservice.elseBlock(geoLocation);
}
}
});
You could also solve this problem like this (and if I understand your problem correctly, this is probably the preferred method):
app.service('myservice', function(){
return {
go: function(geoLocation){
if(condition){
// code to run if condition is true
} else {
// code to run if condition is false
}
}
}
});
app.controller('main', function(myservice){
$scope.myevent = function(geoLocation){
myservice.go(goeLocation);
}
});
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
}
}