Alexander Farber
Alexander Farber

Reputation: 22988

Display checkboxes in jQuery UI dialogs and provide Cancel button

I'm trying to solve probably a very common problem and have prepared a simplified test case demonstrating it and my efforts.

I'm trying to display several jQuery UI dialogs, each containing several checkboxes of the same name (fruits and candy in my test code below)

In each dialog I have 4 buttons: Save, Cancel, Select all and Deselect all:

screenshot

The first 3 buttons are already working in my code.

The Update button will in fact call DataTable's fnDraw() function, that part is already working too. (And I don't want to save the checkboxes values on the server inbetween, I'd like to do everything on the client-side - I know, this is possible).

My problem is in implementing the Cancel button for the dialogs:

1) I should probably save a list of currently set checkboxes on the dialog open event? And then restore them on Cancel click? Is there some elegant jQuery-way for that?

2) I don't know, how to only process the checkboxes of the currently open dialog only.

Below is my current test code, it works instantly - thanks to Google CDN:

<html>
<head>
<style type="text/css" title="currentStyle">
        @import "http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.0/themes/redmond/jquery-ui.css";
</style>

<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jqueryui/1/jquery-ui.min.js"></script>
<script type="text/javascript">

$(function() { 
        var buttons = {
                Cancel: cancel,
                Save: save,
                'Deselect All': deselect,
                'Select All': select
        };

        $('#fruits').dialog({ 
                autoOpen: false, 
                modal: true,
                buttons: buttons
        });

        $('#candy').dialog({ 
                autoOpen: false, 
                modal: true,
                buttons: buttons
        });
});

function update() {
        var boxes = new Array();
        $(':checkbox').each(function() {
                if ($(this).is(':checked')) {
                        boxes.push(
                                $(this).attr('name') + 
                                '=' +
                                $(this).val() 
                        );
                }
        });

        alert('boxes: ' + boxes.join('&'));
}

function select() {
        $(':checkbox').prop('checked', true);
}

function deselect() {
        $(':checkbox').prop('checked', false);
}

function save() {
        // XXX how to implement?
        $(this).dialog('close');
}

function cancel() {
        // XXX how to implement?
        $(this).dialog('close');
}

</script>
</head>
<body>

<p><input type="button" value="Select fruits" onclick="$('#fruits').dialog('open');"></p>
<div id="fruits" title="fruits">
<p><label><input type="checkbox" name="fruits" value="apple">apple</label></p>
<p><label><input type="checkbox" name="fruits" value="banana">banana</label></p>
<p><label><input type="checkbox" name="fruits" value="pear">pear</label></p>
</div>

<p><input type="button" value="Select candy" onclick="$('#candy').dialog('open');"></p>
<div id="candy" title="candy">
<p><label><input type="checkbox" name="candy" value="toffee">toffee</label></p>
<p><label><input type="checkbox" name="candy" value="fudge">fudge</label></p>
</div>

<p><input type="button" onclick="update();" value="Update"></p>

</body>
</html>

UPDATE: Thanks to mootinator the following code is working, but I still have 2 minor issues/questions:

1) Is it possible to use open event instead of the custom openDialog()method?

2) My Deselect All and Select All buttons modify all checkboxes at the page - instead of modifying only those belonging to the current dialog. I wonder how to select only the latter ones? (somehow use $(this) in selectAll() and deselectAll())?

I've tried

function selectAll() {
        $($(this) + ' :checkbox').prop('checked', true);
}

function deselectAll() {
        $($(this) + ' :checkbox').prop('checked', false);
}

but get syntax error.

enter image description here

<html>
<head>
<style type="text/css" title="currentStyle">
        @import "/css/demo_table_jui.css";
        @import "http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.0/themes/redmond/jquery-ui.css";
</style>

<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jqueryui/1/jquery-ui.min.js"></script>
<script type="text/javascript" src="/js/jquery.dataTables.min.js"></script>
<script type="text/javascript">

$(function() {
        var buttons = {
                Cancel: cancel,
                Save: save,
                'Deselect All': deselectAll,
                'Select All': selectAll
        };

        $('#openCandy').button();
        $('#openFruits').button();
        $('#update').button();

        $('#openCandy').click(function() {
                openDialog('#candy');
        });

        $('#openFruits').click(function() {
                openDialog('#fruits');
        });

        $('#fruits').dialog({
                autoOpen: false,
                modal:    true,
                buttons:  buttons
        });

        $('#candy').dialog({
                autoOpen: false,
                modal:    true,
                buttons:  buttons
        });
});

function update() {
        var boxes = new Array();
        $(':checkbox').each(function() {
                if ($(this).is(':checked')) {
                        boxes.push(
                                $(this).attr('name') +
                                '=' +
                                $(this).val()
                        );
                }
        });

        alert('boxes: ' + boxes.join('&'));
}

function selectAll() {
        $(':checkbox').prop('checked', true);
}

function deselectAll() {
        $(':checkbox').prop('checked', false);
}

function openDialog(sel) {
    $(sel).dialog('open');
    $(sel + ' :checkbox').each(function() {
        $(this).data('XXX', $(this).is(':checked'));
    });
}

function cancel() {
        $(this).find(':checkbox').each(function() {
            $(this).prop('checked', $(this).data('XXX'));
        });
        $(this).dialog('close');
}

function save() {
        $(this).dialog('close');
}

</script>
</head>
<body>

<p><input id="openFruits" type="button" value="Select fruits"></p>
<div id="fruits" title="fruits">
<p><label><input type="checkbox" name="fruits" value="apple">apple</label></p>
<p><label><input type="checkbox" name="fruits" value="banana">banana</label></p>
<p><label><input type="checkbox" name="fruits" value="pear">pear</label></p>
</div>

<p><input id="openCandy" type="button" value="Select candy"></p>
<div id="candy" title="candy">
<p><label><input type="checkbox" name="candy" value="toffee">toffee</label></p>
<p><label><input type="checkbox" name="candy" value="fudge">fudge</label></p>
</div>

<p><input id="update" type="button" onclick="update();" value="Update"></p>

</body>
</html>

UPDATE2: Actually I have a 3rd and bigger problem: the closing X-button on the top-right corner of the dialog doesn't work as it should (it saves instead of cancelling).

I've tried adding close: cancel, to both dialogs, but I get the runtime error in Chrome:

Uncaught RangeError: Maximum call stack size exceeded
f.event.remove
f.event.remove
f.fn.extend.unbind
a.extend.destroy
a.extend.destroy
a.widget.close
a.widget.bridge.a.fn.(anonymous function)
e.extend.each
e.fn.e.each
a.widget.bridge.a.fn.(anonymous function)
cancel
a.Widget._trigger
a.widget.close
a.widget.bridge.a.fn.(anonymous function)
.....etc....

UPDATE3: Probably because I call $(this).dialog('close') in a loop?

I don't see an easy way to fix it though: if I create a separate function

function restore() {
        $(this).find(':checkbox').each(function() {
            $(this).prop('checked', $(this).data('XXX'));
        });
}

function cancel() {
        restore();
        $(this).dialog('close');
}

and pass it as close: restore to dialogs, then the Save button breaks

Upvotes: 8

Views: 14362

Answers (2)

Kevin Stricker
Kevin Stricker

Reputation: 17388

I made a couple of modifications to your code. Probably the easiest thing to do (not necessarily the best) would be to save the state in a global variable. (A more robust solution might just involve saving the state alongside the dialog state/options).

UPDATE: Cleaned up the solution to store the initial state on the checkbox itself in the DOM instead of in a global variable.

http://jsfiddle.net/rhdNH/8/

I added a custom open function to facilitate saving the state, and completed the cancel function. You may want to also clean up the 'original' properties whenever you close using removeProp, but it isn't strictly necessary.

Here are the changes you would make for point #1 (open:openDialog) in order to not have to call openDialog directly.

$('#fruits').dialog({ 
    autoOpen: false, 
    modal: true,
    buttons: buttons,
    open: openDialog
});

$('#candy').dialog({ 
    autoOpen: false, 
    modal: true,
    buttons: buttons,
    open: openDialog
});

And this is what your select/deselect should look like to only act on the open dialog:

function select() {
    $(':checkbox', this).prop('checked', true);
}

function deselect() {
    $(':checkbox', this).prop('checked', false);
}

And here is openDialog edited to use this instead of the jQuery selector:

function openDialog() {
    $(':checkbox', this).each(function() {
        $(this).prop('original', $(this).is(':checked'));
    });
}

function cancel() {
        $(this).find('input[type="checkbox"]').each(function() {
            $(this).prop('checked', $(this).prop('original'));
        });
        $(this).dialog('close');
}

Upvotes: 1

matino
matino

Reputation: 17725

  1. In your save function you should call your server side script (using AJAX - check jQuery docs here) and save the state of each checkbox (if this is what you're willing to do of course)
  2. In cancel function I'd just stick with closing the dialog, since you don't need to change anything. Rolling back the changes made by save is pointless since you can always check / unckech desired checkboxes and save them again.
  3. How to only process the checkboxes of the currently open dialog only - assign a unique id to the div container and use $('#div_id input[type="checkbox"]') selector

Upvotes: 0

Related Questions