Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow setting select data without losing the current selection. #1497

Closed
crud89 opened this issue Feb 7, 2020 · 5 comments
Closed

Allow setting select data without losing the current selection. #1497

crud89 opened this issue Feb 7, 2020 · 5 comments

Comments

@crud89
Copy link
Contributor

crud89 commented Feb 7, 2020

First of all, I want to thank you for your awesome work. MetroUI has made my life easier for a few years now. The current version is a blast and it looks gorgeous.

Now to my suggestion: I recently built upon the Ajax Select sample to create a server-side suggestion list, i.e. the user types in some string and the server returns a JSON-list of suggestions using AJAX. I found this a little bit cumbersome, since while it is possible to set the select options using .data(...), it completely rebuilds the options list, which also removes the previously selected items (in case of a multiple select).

I came up with a solution that caches the current selection manually:

var selectControl = Metro.getPlugin('#select1', 'select');
var element = selectControl.element;
var input = selectorContainer.closest('.select').find('.drop-container input[type="text"]');

// Hook typing event of filter input.
input.on('input', function () {
        // Store the current selection.
        var selectedKeys = selectControl.getSelected();
        var currentSelection;

        selectedKeys.forEach(function (key) {
            currentSelection[key] = $(element).find('option[value="' + key + '"]').text();
        });

        // Post an ajax request to the server.
        $.ajax({
            url: '/suggest',
            method: "GET",
            parseJson: true,
            data: {
                filter: $(this).val(),                  // Current filter text
                except: JSON.stringify(selectedKeys)    // Currently selected keys, that should not be included in the response.
            }
        }).then(
            function (response) {
                // Parse the response
                var suggestions = JSON.parse(response);

                // Add the current selection back to the suggestions.
                selectedKeys.forEach(function (key) {
                    suggestions[key] = currentSelection[key];
                });

                // Build the selection list.
                selectControl.data(suggestions);

                // Select all previously selected keys.
                selectControl.val(selectedKeys);
            },
            function (xhr) {
                console.log(xhr.status, xhr.statusText);
            }
        );
    }
});

There are two things that I think would greatly improve the select control. First of all (and my main point, as the title suggests), I would love to be able to set the response and keep the currently selected options. The select control already stores them, but is getting reset with setting the data. Maybe a boolean flag could be added, which already reduces the code to something like this:

var selectControl = Metro.getPlugin('#select1', 'select');
var input = selectorContainer.closest('.select').find('.drop-container input[type="text"]');

// Hook typing event of filter input.
input.on('input', function () {
        // Post an ajax request to the server.
        $.ajax({
            url: '/suggest',
            method: "GET",
            parseJson: true,
            data: {
                filter: $(this).val(),
                except: JSON.stringify(selectControl.getSelected())
            }
        }).then(
            function (response) {
                // Second parameter could be used to keep currently selected values.
                selectControl.data(JSON.parse(response), true);
            },
            function (xhr) {
                console.log(xhr.status, xhr.statusText);
            }
        );
    }
});

Second, I think it would be nice to have some built-in events to hook filtering (e.g. onFiltering which hooks into the filter-control input event and onFiltered, which could be executed, after the filtered results are shown). This would further simplify the code above:

// Hook typing event of filter input.
Metro.getPlugin('#select1', 'select').on('filtering', function (filterString) {
        // Post an ajax request to the server.
        $.ajax({
            url: '/suggest',
            method: "GET",
            parseJson: true,
            data: {
                filter: filterString,
                except: JSON.stringify($(this).getSelected())
            }
        }).then(
            function (response) {
                selectControl.data(JSON.parse(response), true);
            },
            function (xhr) {
                console.log(xhr.status, xhr.statusText);
            }
        );
    }
});

Thanks in advance and keep up your work! 👍

@olton
Copy link
Owner

olton commented Jul 11, 2020

In 4.3.10 I added

  • Select: for API method data(newOptions, selectedValues) added second argument. Must be a string or array
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <link href="../metro/css/metro-all.css?ver=@@b-version" rel="stylesheet">

    <title>Select set data test - Metro 4 :: Popular HTML, CSS and JS library</title>

</head>
<body class="m4-cloak">
<div class="container">
    <h1>Select set data test page</h1>

    <div>
        <button class="button" onclick="loadData()">Load data</button>
    </div>

    <div class="row">
        <div class="cell-md-6">
            <select id="sel1" data-role="select">
                <option value="1">Item 1</option>
                <option value="2" selected>Item 2</option>
                <option value="3">Item 3</option>
            </select>
        </div>
        <div class="cell-md-6">
            <select id="sel2" data-role="select" multiple>
                <option value="1">Item 1</option>
                <option value="2" selected>Item 2</option>
                <option value="3" selected>Item 3</option>
            </select>
        </div>
    </div>
</div>

<script src="../metro/js/metro.js?ver=@@b-version"></script>
<script>
    function loadData(){
        var data = {
            1: "Item 1",
            "2": "Item 2",
            3: "Item 3",
            4: "Item 4",
            5: "Item 5",
        };
        var selected1 = [2];
        var selected2 = [2, 3, 4];

        Metro.getPlugin("#sel1", "select").data(data, selected1);
        Metro.getPlugin("#sel2", "select").data(data, selected2);
    }
</script>
</body>
</html>

olton added a commit that referenced this issue Jul 11, 2020
…d argument. Must be a `string` or `array`, issue #1497
@olton olton added this to the 4.3.10 milestone Jul 11, 2020
@olton olton closed this as completed Jul 13, 2020
@crud89
Copy link
Contributor Author

crud89 commented Jul 15, 2020

Thank you very much! 😊 I will update my version and try this out, when I've got time.

Also, I've send you a donation. Hope it helps you! 💸

@olton
Copy link
Owner

olton commented Jul 15, 2020

Thank you for your donation ❤️. Could you clarify the details of the donation, I can not find the transaction. Thanks you.

@crud89
Copy link
Contributor Author

crud89 commented Jul 15, 2020

I've used the SEPA EURO bank account from your support page (GB61CLJU00997180121989). It should be transferred in a couple of days.

@olton
Copy link
Owner

olton commented Jul 15, 2020

Thank you my friend ❤️

olton added a commit that referenced this issue Oct 28, 2023
…d argument. Must be a `string` or `array`, issue #1497
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants