Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 373
Alexander Farber Avatar asked Oct 04 '11 12:10

Alexander Farber


1 Answers

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');
}
like image 134
Kevin Stricker Avatar answered Nov 15 '22 04:11

Kevin Stricker