AngularJS How to restrict double/multiple click - javascript

I know a solution for double/multi click on angularJS that disable on button click & enable it after completing ajax processing using ng-disabled. Please suggest any other best solution for handling double click in angularJS...
Our code will call ajax method on button click & will take small amount of tme to process & get data from db. We have to restrict second/ multi clicks mean while.
I don't want to allow click when ajax in progress...
Welcome Please be simple solutions rather than using ng-disabled, I am learner of AngularJS
createDialogService('ajs/common/templates/popup/config_reminder_popup.html',
{
title: isFrom,
backdrop: true,
controller: 'configRemindersCBController',
footerTemplate: '' + action + ''
});
$scope.saveOrUpdateReminder = function (reminder)
{
if ($scope.isDisabled)
{
return;
}
$scope.isDisabled = true;
if (!reminder.daysBeforeAfterCheckDate || reminder.daysBeforeAfterCheckDate === '')
{
alertService.openValidatPopup('Please enter days before expiration.', "Error", true, 'configRemindersCBController', 'Ok', 'u1_remove.png');
$scope.isDisabled = false;
return;
}
configRemindersService.isDaysBeforeAfterCheckDate($scope.objectId, reminder, function (result)
{
if (!reminder.selectedMessageTemplate.messageId || reminder.selectedMessageTemplate.messageId === '')
{
alertService.openValidatPopup('Please select message template.', "Error", true, 'configRemindersCBController', 'Ok', 'u1_remove.png');
$scope.isDisabled = false;
return;
}
else if (!reminder.selectedReminderSendOption.reminderSendOptionValue || reminder.selectedReminderSendOption.reminderSendOptionValue === '')
{
alertService.openValidatPopup('Please select reminder send option.', "Error", true, 'configRemindersCBController', 'Ok', 'u1_remove.png');
$scope.isDisabled = false;
return;
}
var enableReminder;
if (result.Result === 'No')
{
if (reminder.enable === true)
{
enableReminder = 'Enable';
}
else
{
enableReminder = 'Disable';
}
configRemindersService.addOrUpdateReminderConfigLine($scope.objectId, reminder, enableReminder, function (remindersResponse)
{
var reminder = remindersResponse.reminderConfigLine;
$rootScope.CONFIG = JSON.parse(remindersResponse.configData);
$scope.$modalClose();
$scope.isDisabled = false;
_.filter(configRemindersService.getMessageTemplates(), function (msg)
{
if (reminder.messageTemplateId === msg.messageId)
{
reminder.selectedMessageTemplate = msg;
}
});
_.filter(configRemindersService.getReminderSendOptions(), function (option)
{
if (reminder.reminderSendOption === option.reminderSendOptionValue)
{
reminder.selectedReminderSendOption = option;
}
});
if (configRemindersService.getIsFrom() === 'Create Reminder')
{
configRemindersService.getReminders().push(reminder);
}
else
{
configRemindersService.getReminders()[configRemindersService.getIndex()] = reminder;
}
});
}
});
};

ng-disabled is definitely the proper way to go with this -- it's a ready-made directive for disabling an object under certain conditions.
If you need to disable it without it looking disabled, you have 2 options:
Change the css for the disabled state to look like it's enabled.
Mimic this with a scope variable that only performs the actions when set.
For the second:
$scope.stopClick = false;
$scope.buttonClick = function () {
if ($scope.stopClick) { return; }
$scope.stopClick = true;
<.. code ..>
callback: function(data) {
$scope.stopClick = false;
}
};
This will accomplish the goal, but it's reinventing the wheel and is likely less robust than just disabling the element and restyling it.

Well you could do something like this :
Create a $scope variable
$scope.myFunnyPromise = null;
in the template :
<button ng-if="myFunnyPromise == null"></button>
Only show the button if myFunnyPromise equals null.
And in your function :
if($scope.myFunnyPromise !== null){
return $scope.myFunnyPromise;
}
$scope.myFunnyPromise = asyncTask().finally(function cleanUp(){
//clear the $scope variable
$scope.myFunnyPromise = null;
})

You can have a function in $rootScope which contains information about whether there is an ongoing http request. Then you can use that value to disable the buttons that you don't want successive click events.
I usually use fieldset attribute to disable input fields while there is an ongoing http request.
<fieldset ng-disabled="isWaitingForServerResponse()"> //when there is an ongoing request, input fields under this tag will be disabled </fieldset>
For implementing isWaitingForServerResponse I got help from a busy bar implementation, which shows a loading bar while there is a http request. It creates events when there is a new request and another request when it stops. So in these events I incremented a counter which holds the number of active http requests and decrement it for each http response. I haven't used it before but I guess you can also use the $http.pendingRequests property too for understanding whether there is a pending http request.
$rootScope.numberOfResponseWaitingFromServer = 0;
$rootScope.$on("cfpLoadingBar:loading", function (event) {
$rootScope.numberOfResponseWaitingFromServer++;
});
$rootScope.$on("cfpLoadingBar:loaded", function (event) {
$rootScope.numberOfResponseWaitingFromServer--;
});
$rootScope.isWaitingForServerResponse = function () {
return $rootScope.numberOfResponseWaitingFromServer > 0;
}

Related

WinJS listview iteminvokedHanlder how to

I'm using the iteminvokedHandler and was wonder if there is a better way to interact with the listView.
Currently using this:
WinJS.UI.processAll(root).then(function () {
var listview = document.querySelector('#myNotePad').winControl;
listview.addEventListener("iteminvoked", itemInvokedHandler,false);
function itemInvokedHandler(e) {
e.detail.itemPromise.done(function (invokedItem) {
myEdit();
});
};
});
The problem is that everytime I click on the listview myEdit() is run and propagates within the listview. I was wondering how to do it once and stop invoking listview until I am done with myEdit? Is there a simpler way to handle such a situation as this?
Simple yet hard to see when you have a mind block and forget some of the basics (yes yes I'm still learning):
var testtrue = true;
WinJS.UI.processAll(root).then(function () {
var listview = document.querySelector('#myNotePad').winControl;
listview.addEventListener("iteminvoked", itemInvokedHandler,false);
function itemInvokedHandler(e) {
e.detail.itemPromise.done(function (invokedItem) {
if (testtrue === true){
myEdit();
}
});
};
});
In myEdit:
function myEdit() {
var theelem = document.querySelector(".win-selected #myNotes");
var gestureObject = new MSGesture();
gestureObject.target = theelem;
theelem.gestureObject = gestureObject;
theelem.addEventListener("pointerdown", pointerDown, false);
theelem.addEventListener("MSGestureHold", gestureHold, false);
function pointerDown(e) {
e.preventDefault();
e.target.gestureObject.addPointer(e.pointerId);
}
function gestureHold(e) {
if (e.detail === e.MSGESTURE_FLAG_BEGIN && test === true) {
e.preventDefault();
editNotes();
} else {
}
console.log(e);
}
theelem.addEventListener("contextmenu", function (e) {
e.preventDefault();}, false); //Preventing system menu
};
function editNotes() {
//The Code I wish to execute
return test = false;
};
What I needed was a conditional statement so that it would run if true and not if false. That same test needed to be done in the gestureHold otherwise it would continue to fire myEdit on the invoked item because of the way the gesture is attached to the item the first time it is run.

Template.onRendered() called too early

I have hit a strange problem. I am using hammer.js to configure long-press events, and attaching the event watcher within the Template.onRendered() callback. This works perfectly on my local development machine. However, when I deploy to a server, it seems that onRendered() is being fired before the template is finished rendering in the browser. The jQuery selector is empty. I've had to resort to setting a timer to make sure the template is rendered before configuring the event handler. Alternatively, I've tried configuring the event handler within a Tracker.afterFlush() in place of setTimeout(), but the template is still not rendered when this fires.
It seems that I shouldn't have to use setTimeout() here. Am I doing something wrong or out of order?
Template.CATEGORIES.onRendered(function () {
Meteor.setTimeout(function () {
console.log('setting up hammer object', this.$('.collapsible'));
var h = this.$('.collapsible').hammer({domEvents:true});
h.on('tap press swipe pan', '.collapsible-header', function (ev) {
// get the ID of the shopping list item object
var target = ev.target;
var $target = $(target);
var type = ev.type;
var $header = $target;
if (Collapse.isChildrenOfPanelHeader($target)) {
$header = Collapse.getPanelHeader($target);
}
console.log('Firing ', type, ' on ', $header);
Kadira.trackError('debug', 'Firing ' + type + ' on ' + $header);
// handler for checkbox
if (type === 'tap' && $target.is('label')) {
var data = Blaze.getData(target);
var TS = data.checkedTS;
ev.preventDefault();
data.checked = !data.checked;
console.log('Checkbox handler', data);
if (data.checked && !TS) {
TS = new Date()
} else if (!data.checked) {
TS = null
}
// Set the checked property to the opposite of its current value
ShoppingList.update(data._id, {
$set: {checked: data.checked, checkedTS: TS}
});
} else if (type === 'tap' && $target.has('.badge')) {
// if the user taps anywhere else on an item that has a child with .badge class
// this item has deals. Toggle the expand.
console.log('badge handler');
$header.toggleClass('active');
Collapse.toggleOpen($header);
} else if (type === 'press' || type === 'swipe' || type === 'pan') {
// remove any selected deals
var itemID, item;
var $label = $header.find('label');
console.log('long press handler');
if ($label) {
itemID = $label.attr('for');
item = ShoppingList.findOne(itemID);
if (item && item.deal) {
Deals.update(item.deal._id, {$set: {'showOnItem': false}});
ShoppingList.update(itemID, {$set: {'deal': null}});
}
}
}
})
}, 2000);
});
Someone answered this the other day but must have withdrawn their answer. They suggested that the template was actually rendered, but that the data subscription had not finished replicating yet. They were right.
I was able to validate that I need to wait until the data subscription finishes prior to setting up my java script events.

Returning true on Link Click Event isn't working

I am trying to execute some code that has a callback when clicking on specific links. The callback is to click the link again, but on the second pass, the method returns true to follow normal behavior. But for some reason, it's not working? I use clickedLink for proper scope. In the event callback, this was referring to window.
UPDATE
For a little more clarity, I agree normally this wouldn't be an optimal solution, but I am using Google's Tag Manager to monitor eCommerce traffic. I am trying to make sure product clicks get pushed to their dataLayer, and using their event callback to resume normal behavior. More info here: https://developers.google.com/tag-manager/enhanced-ecommerce#product-clicks
This is my updated method based on the answers below, but it still doesn't work.
var shopCartBtnClicked = false;
var clickedLink;
jQuery('#pdp_add_cart, .add_to_cart_button').click(function(e) {
if (shopCartBtnClicked === true) {
shopCartBtnClicked = false; //I make it here just fine
} else {
e.preventDefault();
clickedLink = this;
var pdp = false;
if (mmProduct) {
//On detail page
mmProduct.qty = jQuery('input[name="quantity"]').val();
pdp = true;
} else {
//on a shopping page
mmProduct = findProductClicked(this);
mmProduct.qty = 1;
}
dataLayer.push({
'event': 'addToCart',
'ecommerce': {
'currencyCode': 'USD',
'add': {
'products': [{
'name': mmProduct.name,
'id': mmProduct.id,
'price': mmProduct.price,
'quantity': mmProduct.qty
}]
}
},
'eventCallback': function () {
//Are we on a product detail page with a form, or just a shopping page with a link?
if (pdp) {
jQuery('form.cart').submit(); //This part works just fine
} else {
mmProduct = null;
shopCartBtnClicked = true;
$(clickedLink).trigger('click'); //This doesn't
}
}
});
}
});
Its not very well done, but this should work:
var shopCartBtnClicked = false;
var clickedLink;
jQuery('.add_to_cart_button').click(function(e) {
if (shopCartBtnClicked === true) {
shopCartBtnClicked = false;
// dont return, just let javascript handle this one
} else {
// only execute when you have not set it to true
e.preventDefault();
clickedLink = this;
shopCartBtnClicked = true;
$(clickedLink).trigger('click');
}
});
I do have to wonder why you don't just execute your other logic first and then not prevent default anyway.
Taking #somethinghere's answer, your code can further be simplified to improve readability:
var shopCartBtnClicked = false;
jQuery('.add_to_cart_button').click(function(e) {
if( shopCartBtnClicked ) {
shopCartBtnClicked = false;
// dont return, just let javascript handle this one
} else {
// only execute when you have set it to true
e.preventDefault();
shopCartBtnClicked = true;
this.click();
}
});
Or, as suggested by #Regent:
var shopCartBtnClicked = false;
jQuery('.add_to_cart_button').click(function(e) {
shopCartBtnClicked = !shopCartBtnClicked;
if( shopCartBtnClicked ) {
e.preventDefault();
this.click();
}
});
OK guys, thank you for helping me get there. Normally, all of the other answers would work great, but for this specific tag manager instance, it appears (for some unknown reason), document.location works in the event callback fine here. This works.
It's weird because I used $(this).('form.cart').submit(); in a callback earlier in the code.
'eventCallback': function () {
//Are we on a product detail page with a form, or just a shopping page with a link?
if (pdp) {
jQuery('form.cart').submit();
} else {
mmProduct = null;
document.location = $(clickedLink).attr('href');
//$(clickedLink).trigger('click');
}
}

how to toggle ng-click

hello guys i'm trying to implement Upvote feature but i'm having some difficulties to get it toggle, by toggle i mean when user click the button/link it will perform upvote if the user click it again it will remove the upvote, just like a toggle button but i can't seem to find any relevant info from google. this is what i have so far
UPDATED
$scope.up = function(){
if(s_session != null){ // check user is logged in or not
data.getUserData().success(function(userData){ // get user data
$scope.a = data1.votes.up; // this is the data from database
// is a list of username that who voted this post
$scope.b = userData.publicUsername.display;// this is the current username
if($scope.a.indexOf($scope.b) == -1){ //check this username whether is in
// the list if -1 mean not
$scope.user ={};
$scope.user = userData;
$scope.user.permalink = $routeParams.permalink;
$http.post('/contentHandler/post/vote/up',$scope.user). // perform upvote
success(function(updatedData) {
$scope.votedMsg="VOTED";
$scope.afterVoteUp={'color':'#B3B3B3'}
}).error(function(err){
});
}else{ //else remove upvote
$scope.user ={};
$scope.user = userData;
$scope.user.permalink = $routeParams.permalink;
$scope.votedMsg="";
$scope.afterVoteUp={'color':'#2ecc71'}
$http.post('/contentHandler/post/vote/up/remove',$scope.user).
success(function(Data) {
}).error(function(err){
});
}
});
}else{
$location.path('/login'); //redirect to login if not logged in
}
}
but problem is i can't click the button twice, the first click will perform upvote but the second click still will perform upvote, the problem is because the list of username is not being updated, but if i refresh the page and click it again it will remove the upvote. may i is there any better way to do this ?
A simple approach would be to maintain the state in a variable, but please read below for caveats
$scope.upVoted = false
$scope.up = function() { //ng-click
if ($scope.upVoted) {
$http.post('/contentHandler/post/vote/up', $scope.user).
success(function(updatedData) {
$scope.votedMsg = "VOTED";
$scope.afterVoteUp = {
'color': '#B3B3B3'
}
}).error(function(err) {
});
}else{
$http.post('/contentHandler/post/vote/up/remove', $scope.user).
success(function(updatedData) {
$scope.votedMsg = "VOTE REMOVED";
$scope.afterVoteUp = {
'color': '#FFFFFF'
}
}).error(function(err) {
});
}
});
The potential issue you have with this and all frontend approaches is that the user can trivially change the value of upVoted, and vote again.
Assuming you're doing some validation in /contentHandler/post/vote/up you could alter it to toggle the vote on the server. That way the response could be the updated voted status from the server.
If you manage the state on the frontend, how will you know if the user has voted when they refresh the page?
A clean approach would be:
<div ng-click="isUpVoted = !isUpVoted">{{isUpVoted}}</div>
JS:
$scope.isUpVoted = false;
$scope.$watch("isUpVoted",function(newVal, oldVal){
if(newVal != oldVal){
if(newVal){
// perform upvote
}
else{
// perform remove upvote
}
}
});
Fiddle
I don't know AngularJS, but basic programming knowledge and assuming .up is the click handler tells me you need:
$scope.up = function() {
if( {ALREADY_UP_VOTED} ) {
//perform remove upvote
} else {
//perform upvote
}
}
EDIT: (with dfsq's comment in mind)
I guess you could look at your $scope.votedMsg variable:
$scope.up = function() {
if( $scope.votedMsg == "VOTED" ) {
// perform remove upvote
// remove upvote thing..
$scope.votedMsg = "";
} else {
//perform upvote
}
}

Running a form handled by ajax in a loaded ajax page?

Using tutorials found i'm currently loading new pages with this:
$("a.nav-link").click(function (e) {
// cancel the default behaviour
e.preventDefault();
// get the address of the link
var href = $(this).attr('href');
// getting the desired element for working with it later
var $wrap = $('#userright');
$wrap
// removing old data
.html('')
// slide it up
.hide()
// load the remote page
.load(href + ' #userright', function () {
// now slide it down
$wrap.fadeIn();
});
});
This loads the selected pages perfectly, however the pages have forms that themselves use ajax to send the following:
var frm = $('#profileform');
frm.submit(function (ev) {
$.ajax({
type: frm.attr('method'),
url: frm.attr('action'),
data: frm.serialize(),
success: function (data) {
alert(data)
}
});
However this is not sending the form as it did before the page itself was called to the parent page via ajax. Am I missing something? Can you not use an ajax call in a page already called by ajax?
I also have other issues, for example I disable the submit button unless there are any changes to the form, using:
var button = $('#profile-submit');
var orig = [];
$.fn.getType = function () {
return this[0].tagName == "INPUT" ? $(this[0]).attr("type").toLowerCase() : this[0].tagName.toLowerCase();
}
$("#profileform :input").each(function () {
var type = $(this).getType();
var tmp = {
'type': type,
'value': $(this).val()
};
if (type == 'radio') {
tmp.checked = $(this).is(':checked');
}
orig[$(this).attr('id')] = tmp;
});
$('#profileform').bind('change keyup', function () {
var disable = true;
$("#profileform :input").each(function () {
var type = $(this).getType();
var id = $(this).attr('id');
if (type == 'text' || type == 'select') {
disable = (orig[id].value == $(this).val());
} else if (type == 'radio') {
disable = (orig[id].checked == $(this).is(':checked'));
}
if (!disable) {
return false; // break out of loop
}
});
button.prop('disabled', disable);});
However this also doesn't work when pulled to the parent page. Any help much appreciated! I'm really new to ajax so please point out any obvious mistakes! Many thanks in advance.
UPDATE
Just an update to what i've found. I've got one form working by using:
$(document).on('mouseenter', '#profile', function() {
However the following:
$(document).on('mouseenter', '#cancelimage', function() {
$('#cancelimage').onclick=function() {
function closePreview() {
ias.cancelSelection();
ias.update();
popup('popUpDiv');
$('#imgForm')[0].reset();
} }; });
Is not working. I understand now that I need to make it realise code was there, so I wrapped all of my code in a mouseover for the new div, but certain parts still don't work, so I gave a mouseover to the cancel button on my image form, but when clicked it doesn't do any of the things it's supposed to.
For anyone else who comes across it, if you've got a function name assigned to it, it should pass fine regardless. I was trying to update it, and there was no need. Doh!
function closePreview() {
ias.cancelSelection();
ias.update();
popup('popUpDiv');
$('#imgForm')[0].reset();
};
Works just fine.

Categories

Resources