The confirm box only has two options: ok and cancel.
I'd like to make one myself, so I can add a third button: save and continue. But the issue I currently don't know how to solve, is that: once the custom confirm dialog is up, how do I block the previously running script (or navigation) from running? and then how do I make the buttons return values for the confirmation?
my understanding of the confirm dialog box is this:
it's a visual boolean, that has the power to block navigation and scripts on a page. So, how do I emulate that?
If you want a reliable proven solution... Use jQuery... it'll work on every browser without worrying about crappy IE etc. http://jqueryui.com/demos/dialog/
In javascript, you don't stop while you're waiting for a user action : you set a callback (a function) that your dialog will call on close.
Here's an example of a small dialog library, where you can see how callbacks can be passed.
dialog = {};
dialog.close = function() {
if (dialog.$div) dialog.$div.remove();
dialog.$div = null;
};
// args.title
// args.text
// args.style : "", "error" (optionnel)
// args.buttons : optional : map[label]->function the callback is called just after dialog closing
// args.doAfter : optional : a callback called after dialog closing
dialog.open = function(args) {
args = args || {};
if (this.$div) {
console.log("one dialog at a time");
return;
}
var html = '';
html += '<div id=dialog';
if (args.style) html += ' '+args.style;
html += '><div id=dialog-title>';
html += '</div>';
html += '<div id=dialog-content>';
html += '</div>';
html += '<div id=dialog-buttons>';
html += '</div>';
html += '</div>';
this.$div=$(html);
this.$div.prependTo('body');
$('#dialog-title').html(args.title);
$('#dialog-content').html(args.text);
var buttons = args.buttons || {'Close': function(){return true}};
for (var n in buttons) {
var $btn = $('<input type=button value="'+n+'">');
$btn.data('fun', buttons[n]);
$btn.click(function(){
if ($(this).data('fun')()) {
dialog.close();
if (args.doAfter) args.doAfter();
}
});
$btn.appendTo($('#dialog-buttons'));
}
this.$div.show('fast');
shortcuts.on('dialog', {
27: function(){ // 27 : escape
dialog.close();
}
});
}
Two call samples :
dialog.open({
title: 'ccccc Protection Error',
text: 'There was an error related to cccc Protection. Please consult <a href=../cccc.jsp>this page</a>.',
style: 'error'
});
var ok = false;
dialog.open({
title: sometitle,
text: someHtmlWithInputs,
buttons: {
'OK': function() {
if (// inputs are valid) ok = true;
return true;
},
'Cancel': function() {
return true;
}
},
doAfter: function() {
if (ok) {
if (newvg) {
cccmanager.add(vg);
} else {
cccmanager.store();
}
if (doAfter) doAfter();
}
}
});
As specified by others, you may not need your own library if you just want to make a dialog.
Related
I've been looking around but have found no where that explains how to create a custom input field on the toolbar of froala editor. Something similar to how the url button works:
Froala url input
It has an input field and an insert, how can I add a button with similar functionality
You could use the Custom Popup example as a starting point and extend from there by changing the initPopup method like this:
// Popup buttons.
var popup_buttons = '';
// Create the list of buttons.
if (editor.opts.popupButtons.length > 1) {
popup_buttons += '<div class="fr-buttons">';
popup_buttons += editor.button.buildList(editor.opts.popupButtons);
popup_buttons += '</div>';
}
// Custom layer.
var custom_layer = '<div class="fr-my-layer fr-layer fr-active" id="fr-my-layer-' + editor.id + '"><div class="fr-input-line"><input id="fr-my-layer-text-' + editor.id + '" type="text" placeholder="' + editor.language.translate('Alternate Text') + '" tabIndex="1"></div><div class="fr-action-buttons"><button type="button" class="fr-command fr-submit" data-cmd="myButton" tabIndex="2" role="button">' + editor.language.translate('Insert') + '</button></div></div>';
// Load popup template.
var template = {
buttons: popup_buttons,
custom_layer: custom_layer
};
// Create popup.
var $popup = editor.popups.create('customPlugin.popup', template);
return $popup;
Here is my take on using Froala's example and st3fan's suggestion. I wasn't sure how to use st3fan's Insert button, so I repurposed the closePopup button. In this case, I am wrapping the user's text with pre tags and inserting it into the editor.
// Define popup template.
$.extend($.FroalaEditor.POPUP_TEMPLATES, {
'customPlugin.popup': '[_BUTTONS_][_CUSTOM_LAYER_]'
});
// Define popup buttons.
$.extend($.FroalaEditor.DEFAULTS, {
popupButtons: ['popupClose', '|', 'popupButton1', 'popupButton2'],
});
// The custom popup is defined inside a plugin (new or existing).
$.FroalaEditor.PLUGINS.customPlugin = function (editor) {
// Create custom popup.
function initPopup () {
// Load popup template.
var template = $.FroalaEditor.POPUP_TEMPLATES.customPopup;
if (typeof template == 'function') template = template.apply(editor);
// Popup buttons.
var popup_buttons = '';
// Create the list of buttons.
if (editor.opts.popupButtons.length > 1) {
popup_buttons += '<div class="fr-buttons">';
popup_buttons += editor.button.buildList(editor.opts.popupButtons);
popup_buttons += '</div>';
}
// Custom layer.
var custom_layer = '<div class="fr-my-layer fr-layer fr-active" id="fr-my-layer-' + editor.id + '"><div class="fr-input-line"><textarea id="fr-my-layer-text-' + editor.id + '" placeholder="' + editor.language.translate('Alternate Text') + '" tabIndex="1"></textarea></div></div>';
// Load popup template.
var template = {
buttons: popup_buttons,
custom_layer: custom_layer
};
// Create popup.
var $popup = editor.popups.create('customPlugin.popup', template);
return $popup;
}
// Show the popup
function showPopup () {
// Get the popup object defined above.
var $popup = editor.popups.get('customPlugin.popup');
// If popup doesn't exist then create it.
// To improve performance it is best to create the popup when it is first needed
// and not when the editor is initialized.
if (!$popup) $popup = initPopup();
// Set the editor toolbar as the popup's container.
editor.popups.setContainer('customPlugin.popup', editor.$tb);
// If the editor is not displayed when a toolbar button is pressed, then set BODY as the popup's container.
// editor.popups.setContainer('customPlugin.popup', $('body'));
// Trigger refresh for the popup.
// editor.popups.refresh('customPlugin.popup');
// This custom popup is opened by pressing a button from the editor's toolbar.
// Get the button's object in order to place the popup relative to it.
var $btn = editor.$tb.find('.fr-command[data-cmd="myButton"]');
// Compute the popup's position.
var left = $btn.offset().left + $btn.outerWidth() / 2;
var top = $btn.offset().top + (editor.opts.toolbarBottom ? 10 : $btn.outerHeight() - 10);
// Show the custom popup.
// The button's outerHeight is required in case the popup needs to be displayed above it.
editor.popups.show('customPlugin.popup', left, top, $btn.outerHeight());
}
// Hide the custom popup.
function hidePopup () {
var html = $("#fr-my-layer-text-" + editor.id).val();
html = '<br/><pre>' + html + '</pre><br/>';
editor.html.insert(html);
editor.popups.hide('customPlugin.popup');
}
// Methods visible outside the plugin.
return {
showPopup: showPopup,
hidePopup: hidePopup
}
}
// Define an icon and command for the button that opens the custom popup.
$.FroalaEditor.DefineIcon('buttonIcon', { NAME: 'star'})
$.FroalaEditor.RegisterCommand('myButton', {
title: 'Show Popup',
icon: 'buttonIcon',
undo: false,
focus: false,
popup: true,
// Buttons which are included in the editor toolbar should have the plugin property set.
plugin: 'customPlugin',
callback: function () {
if (!this.popups.isVisible('customPlugin.popup')) {
this.customPlugin.showPopup();
}
else {
if (this.$el.find('.fr-marker')) {
this.events.disableBlur();
this.selection.restore();
}
this.popups.hide('customPlugin.popup');
}
}
});
// Define custom popup close button icon and command.
$.FroalaEditor.DefineIcon('popupClose', { NAME: 'plus' });
$.FroalaEditor.RegisterCommand('popupClose', {
title: 'Insert',
undo: false,
focus: false,
refreshAfterCallback: true,
callback: function () {
this.customPlugin.hidePopup();
}
});
jQuery('#fro').froalaEditor({
codeMirror: window.codeMirror,
toolbarButtons: ['bold', 'italic', 'underline', '|', 'myButton', 'html'],
pluginsEnabled: ['customPlugin', 'codeView', 'codeBeautifier']
})
I am landing on the first page of DataTable, make some changes.
Then I move to the second page.
Actually, confirmation popup is shown but it navigate to the second page.
Expected: confirm pop is shown but it still landing on the first.
Here is my code:
$('#dataTable-id').on( 'page.dt', function (event) {
if( change ){
bootbox.dialog({
title: "Confirmation",
message : "Discard changes?",
buttons :{
main: {
label : "Leave",
className : "btn-primary",
callback: function (){
// To avoid broking page/length controller
// move to other pages
return true; // cancel draw
}
},
cancel: {
label : "Stay",
className : "btn-default",
callback : function() {
// stay at current page.
return true;
}
}
},onEscape: function () {return true;}
});
}
});
How to show confirmation popup before page change?
The page.dt event is only informational, it can not be canceled.
You can workaround that restriction by writing a custom preDrawCallback like discussed here: https://datatables.net/forums/discussion/25507
EDIT: You have to cancel the redraw generally and do the paging manually in the bootbox callback (as it does not work as a real modal dialog like the native javascript confirm()). I modified the above example to incorporate a bootbox confirm dialog on paging: https://jsfiddle.net/bk4nvds5/
$(document).ready(function () {
var pageLen = 10;
var originalPage = 0;
var originalPageLen = pageLen;
var gotoPage = 0;
var gotoPageLen = originalPageLen;
var fromBootbox = false;
var table = $('#example').DataTable({
"pageLength": pageLen,
"preDrawCallback": function (settings) {
if(table){ // ignore first draw
if (!fromBootbox) {
// To avoid broking page/length controller, we have to reset the paging information
gotoPage = settings._iDisplayStart;
gotoPageLen = settings._iDisplayLength;
bootbox.confirm("Are you sure?", function(result) {
console.log("goto page" + gotoPage + " (result: " + result + ")");
if (result) {
fromBootbox = true;
table.page.len(gotoPageLen);
table.page(gotoPage / gotoPageLen).draw(false);
fromBootbox = false;
}
});
settings._iDisplayStart = originalPage;
settings._iDisplayLength = originalPageLen;
$('[name="example_length"]').val(originalPageLen);
return false; // cancel draw
}else{
originalPage = settings._iDisplayStart;
originalPageLen = settings._iDisplayLength;
}
}
}
});
});
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;
}
I have JavaScript function:
function validateAddToCartForm(object) {
value = $(".product-detail-qty-box").val();
if(!isInt(value) || value < 1) {
$(".product-detail-error").show();
return false;
} else {
$(".product-detail-error").hide();
var product_name = $("#product_detail_name").text();
var NewDialog = $('<div id="MenuDialog">\ ' + product_name + '</div>');
NewDialog.dialog({
modal: true,
title: "title",
show: 'clip',
hide: {effect: "fadeOut", duration: 1000}
});
}
return true;
}
I need to pause 3 to 5 seconds before returning true, because I want to show a New Dialog box for a while. How can I implement this delay?
The only way to simulate delay in js is callback on timeout.
change the function to:
function validateAddToCartForm(object,callback) {
value = $(".product-detail-qty-box").val();
if(!isInt(value) || value < 1) {
$(".product-detail-error").show();
callback(false);
} else {
$(".product-detail-error").hide();
var product_name = $("#product_detail_name").text();
var NewDialog = $('<div id="MenuDialog">\ ' + product_name + '</div>');
NewDialog.dialog({
modal: true,
title: "title",
show: 'clip',
hide: {effect: "fadeOut", duration: 1000}
});
}
setTimeout(function() {callback(true);},5000);
}
where you call it you should do something like:
instead of
function somefunct() {
//code before call
if (validateAddToCartForm(object)) {
//process true
} else {
//process false
}
//rest of the function
}
place something like:
function somefunct() {
//code before call
validateAddToCartForm(object,function(ret) {
{
if (ret) {
//process true
} else {
//process false
}
//rest of the function
}
}
In to answer to your comment.
I assume:
that you want to prevent click event if validate false,
that all elements that you added onclick="..." have class ".clickme",
the element now looks like
<input type="submit" onclick="return validateAddToCartForm(this)" class="clickme" />
so 1st change the element to
<input type="submit" class="clickme" />
add to your javascript the following:
//this handle original click, drop it out, and only pass after validation
$(function () {
$('.clickme').click(function (e) {
var $t = $(this);
//if event triggered return true
if (e.isTrigger) return true;
validateAddToCartForm(this, function (ret) {
if (ret) {
$t.trigger('click');
}
});
return false;
});
});
also I suggest to use "submit" event on the form itself instead of "click" (the demo of submit)
Instead of blocking, you can use seTimeout to remove the #MenuDialog after a certain time.
function validateAddToCartForm(o){
var keep_dialog_time = 5 * 1000; // five seconds.
// Whatever...
/* Use setTimeout(function, milliseconds) to delay deletion of #MenuDialog.
This will get executed keep_dialog_time milliseconds after this call, and
won't block. */
setTimeout(function(){
$('#MenuDialog').hide(); // maybe there is another function to do this, I'm not a jQuery guy really.
}, keep_dialog_time);
return true;
}
JavaScript is single threaded. This means, when you block, you block the everything. Thus, the DOM uses an event loop model, where callbacks are assigned to events. Such a model is also present in node.js too. Above, because setTimeout does not block, code after that call will continue to run without waiting the function we passed to setTimeout to get executed.
I'd suggest to study DOM in depth to get more comfortable with web front-end programming. You may use various search engines to find out cool documentation.
I have a problem with my work which works good on Firefox and Google Chrome, but it wouldn't work in IE. Can you point out where I'm getting it wrong?
Scripts.js
$(document).ready(function () {
$('.delete').click(function () {
var contentId = $(this).attr('contentid');
$.confirm({
'title': 'Delete Confirmation',
'message': 'Are you sure you want to delete this record?',
'buttons': {
'Yes': {
'class': 'blue',
'action': function () {
DoDelete(contentId);
}
},
'No': {
'class': 'orange',
'action': function () {}
// Nothing to do in this case. You can as well omit the action property.
},
'close': {
'action': function () {}
// Nothing to do in this case. You can as well omit the action property.
}
}
});
});
});
confirmscript to generate HTML markup
(function ($) {
$.confirm = function (params) {
if ($('#confirmOverlay').length) {
return false;
}
var buttonHTML = '';
$.each(params.buttons, function (name, obj) {
buttonHTML += '' + name + '<span></span>';
if (!obj.action) {
obj.action = function () {};
}
});
var markup = [
'<div id="confirmOverlay">',
'<div id="confirmBox">',
'<div id="header">',
'<div id ="title">',
params.title,
'</div></div>',
'<div id ="textbox">',
'<p>',
params.message,
'</p></div>',
'<div id="confirmButtons">',
buttonHTML,
'</div></div></div>'
].join('');
$(markup).hide().appendTo('body').fadeIn();
var buttons = $('#confirmBox .button'),
i = 0;
$.each(params.buttons, function (name, obj) {
buttons.eq(i++).click(function () {
obj.action();
$.confirm.hide();
return false;
});
});
}
$.confirm.hide = function () {
$('#confirmOverlay').fadeOut(function () {
$(this).remove();
});
}
})(jQuery);
Well first off, the first line of your $('.delete') handler is:
var contentId = $(this).attr('contentid');
but your markup for .delete is:
<div class="delete"></div>
(notice the lack of a contentId attribute). Luckily(?) your code doesn't actually seem to use contentId :-)
More relevantly, you need to determine which part of your code is breaking (if you had a JS Fiddle of your code I could look, but since you don't you'll have to find out yourself). I would put an alert just before $.confirm is called, and then again inside $.confirm, to make sure the code is even getting to your plug-in (if not, that's your problem).
If it is, the next question to ask is "is your markup getting added, but not revealed, or is it simply not being added at all". Either alerts or the IE developer tools should let you inspect the DOM and find out if your markup is being added; if not, that's your problem. If it is being added, but isn't getting shown, then something is going wrong with your fadeIn, and that's your problem.
As a final note, there's a simpler way you could hook up your button events; instead of:
var buttons = $('#confirmBox .button'),
i = 0;
$.each(params.buttons, function (name, obj) {
buttons.eq(i++).click(function () {
obj.action();
$.confirm.hide();
return false;
});
});
you could just do:
$('#confirmBox .button').each(function(button) {
$(button).click(function () {
params.buttons[$(this).text()].action();
$.confirm.hide();
return false;
});
});