vtfuture / BForms

Bootstrap Forms for ASP.NET MVC
MIT License
62 stars 33 forks source link

How to do sequential selection with DropDownRemote? #250

Closed mstfcck closed 9 years ago

mstfcck commented 9 years ago
                <!-- User -->
                <div class="col-sm-12 col-md-12 col-lg-12 form-group @Html.BsValidationCssFor(m => m.User)">
                    @Html.BsLabelFor(m => m.User)
                    <div class="input-group">
                        @Html.BsGlyphiconAddon(Glyphicon.User)
                        @Html.BsSelectFor(m => m.User, null, new { url = Url.Action("GetUsers", "UserRoleDefinition", new { area = "Admin" }) })
                        @Html.BsValidationFor(m => m.User)
                    </div>
                </div>

                <!-- Unit -->
                <div class="col-sm-12 col-md-12 col-lg-12 form-group @Html.BsValidationCssFor(m => m.OrganizationUnit)">
                    @Html.BsLabelFor(m => m.OrganizationUnit)
                    <div class="input-group">
                        @Html.BsGlyphiconAddon(Glyphicon.User)
                        @Html.BsSelectFor(m => m.OrganizationUnit, null, new { url = Url.Action("GetUnits", "UserRoleDefinition", new { area = "Admin" }) })
                        @Html.BsValidationFor(m => m.OrganizationUnit)
                    </div>
                </div>

I have two dropdownlist(s) on my form. When I select a user, (after) I want to be search units of selected users (first in dropdownlist). I mean, how to give id of selected user to the unit dropdownlist?

Does anyone have any ideas? Please, enlighten me. Thanks...

mariuscosareanu commented 9 years ago

To achieve this you have to apply select2 with some custom options to the OrganizationUnit dropdown.

  1. First step is to add no-initUI class to OrganizationUnit, like this : @Html.BsSelectFor(m => m.OrganizationUnit, new {@class="js-organizationUnitSelect no-initUI"}, new { url = Url.Action("GetUnits", "UserRoleDefinition", new { area = "Admin" }) }) This will disable the default init for the element. You should also add a class to Users dropdown, let's say js-usersSelect , which will be used in the next step: @Html.BsSelectFor(m => m.User, new {@class="js-usersSelect"}, new { url = Url.Action("GetUsers", "UserRoleDefinition", new { area = "Admin" }) }) 2.Next add this method:
var getSelect2RemoteOptions = function (elem) {
        var $elem = $(elem),
            options = $elem.data('options');

        var s2ropts = {
            placeholder: $elem.attr('placeholder'),
            multiple: $elem.attr('multiple'),
            ajax: {
                url: options.url,
                dataType: 'json',
                data: $.proxy(function (search, page) {
                    return {
                        extraData: options.extraData,
                        search: search,
                        page: page,
                        userId: $('.js-usersSelect').val()
                    };
                }, this),
                results: function (response, page) {

                    var data = response.Data,
                        results = [],
                        more = false;

                    if (data) {

                        if (data.Items) {

                            more = (page * data.PageSize) < data.Count;

                            for (var i = 0; i < data.Items.length; i++) {
                                var item = data.Items[i];

                                results.push({
                                    id: item.Value,
                                    text: item.Text
                                });
                            }
                        }
                    }

                    return { results: results, more: more };
                },
                error: function (jqXHR, status, error) {
                    return {
                        errorMessage: jqXHR.statusText
                    };
                }
            },
            initSelection: function (element, callback) {
                if ($elem.data('init')) {
                    var init = $elem.data('init');
                    callback(init);
                }
            },
            escapeMarkup: function (m) { return m; }
        };

This is almost the same method as the one used by bforms.initUI, with the added userId: $('.js-usersSelect').val() property to the data option at ~ line 16

  1. The last step is to apply select2 to the OrganizationUnit dropdown, like this :
var $userRoleDefinition = $('.js-organizationUnitSelect');
$userRoleDefinition.select2(getSelect2RemoteOptions($userRoleDefinition))

Now the GetUnits method should receive the id of the select user in the userId parameter, allowing you to filter the results.As a heads up, you may also want to reset OrganizationUnit dropdown on User dropdown change, to make sure that the selected value is valid.

mstfcck commented 9 years ago

select2-error

require([ 'jquery', 'jquery-ui', 'bootstrap', 'bforms-validate-unobtrusive', 'bforms-initUI', 'bforms-ajax', 'bforms-resetInput', 'bforms-extensions', 'bforms-inlineQuestion', 'bforms-namespace', 'bforms-select2', 'main-script', 'ebys-common' ], function () {

var UserUnitPositionForm = function (options) {
    this.options = $.extend(true, {}, options);
};

UserUnitPositionForm.prototype.init = function () {
    this.$UserUnitPositionForm = $('.js-formCreate');

    this.$UserUnitPositionForm.bsInitUI(this.options.styleInputs);

    this.addHandlers();
};

UserUnitPositionForm.prototype.addHandlers = function () {
    this.$UserUnitPositionForm.on('click', '.js-buttonCreate', $.proxy(this._onCreate, this));
};

UserUnitPositionForm.prototype._onCreate = function (e) {
// bla. bla...
};

var getSelect2RemoteOptions = function(elem) {
    var $elem = $(elem),
        options = $elem.data('options');

    var s2ropts = {
        placeholder: $elem.attr('placeholder'),
        multiple: $elem.attr('multiple'),
        ajax: {
            url: options.createUrlGetUnitsByUserId, // Get unit bu userId: (public BsJsonResult GetUnitsByUserId(int page, string search, string userId))
            dataType: 'json',
            data: $.proxy(function(search, page) {
                return {
                    extraData: options.extraData,
                    search: search,
                    page: page,
                    userId: $('.js-usersSelect').val()
                };
            }, this),
            results: function(response, page) {

                var data = response.Data,
                    results = [],
                    more = false;

                if (data) {

                    if (data.Items) {

                        more = (page * data.PageSize) < data.Count;

                        for (var i = 0; i < data.Items.length; i++) {
                            var item = data.Items[i];

                            results.push({
                                id: item.Value,
                                text: item.Text
                            });
                        }
                    }
                }

                return { results: results, more: more };
            },
            error: function(jqXHR, status, error) {
                return {
                    errorMessage: jqXHR.statusText
                };
            }
        },
        initSelection: function(element, callback) {
            if ($elem.data('init')) {
                var init = $elem.data('init');
                callback(init);
            }
        },
        escapeMarkup: function(m) { return m; }
    };
};

var $userRoleDefinition = $('.js-organizationUnitSelect');
$userRoleDefinition.select2(getSelect2RemoteOptions($userRoleDefinition));

$(document).ready(function () {
    var ctrl = new UserUnitPositionForm(requireConfig.pageOptions);
    ctrl.init();
});

});

I changed my form controls like you said:

                    <!-- Kullanıcı -->
                    <div class="col-sm-12 col-md-12 col-lg-12 form-group @Html.BsValidationCssFor(m => m.User)">
                        @Html.BsLabelFor(m => m.User)
                        <div class="input-group">
                            @Html.BsGlyphiconAddon(Glyphicon.User)
                            @Html.BsSelectFor(m => m.User, new { @class = "js-usersSelect" }, new { url = Url.Action("GetUsers", "AdminQuery", new { area = "Admin" }) })
                            @Html.BsValidationFor(m => m.User)
                        </div>
                    </div>

                    <!-- Units -->
                    <div class="col-sm-12 col-md-12 col-lg-12 form-group @Html.BsValidationCssFor(m => m.OrganizationUnit)">
                        @Html.BsLabelFor(m => m.OrganizationUnit)
                        <div class="input-group">
                            @Html.BsGlyphiconAddon(Glyphicon.Briefcase)
                            @Html.BsSelectFor(m => m.OrganizationUnit, new { @class = "js-organizationUnitSelect no-initUI" }, new { url = Url.Action("GetUnitsByUserId", "AdminQuery", new { area = "Admin" }) })
                            @Html.BsValidationFor(m => m.OrganizationUnit)
                        </div>
                    </div>

options.createUrlGetUnitsByUserId and Url.Action("GetUnitsByUserId", "AdminQuery", new { area = "Admin" }) are using same url:

public BsJsonResult GetUnitsByUserId(int page, string search, string userId) { // querys... }

But I am getting error like on screenshot... Where I am doing wrong? Thanks for your helps...

mariuscosareanu commented 9 years ago

Yeah, didn't copy the return at the end of getSelect2RemoteOptions, my bad :

var getSelect2RemoteOptions = function (elem) {
    ...
    ...
    return s2ropts;
}

Nevertheless, you should also apply select2 in the dom ready callback :

$(document).ready(function () {
    var ctrl = new UserUnitPositionForm(requireConfig.pageOptions);
    ctrl.init();
    var $userRoleDefinition = $('.js-organizationUnitSelect');
    $userRoleDefinition.select2(getSelect2RemoteOptions($userRoleDefinition));

});

or even better, move it to UserUnitPositionForm (var getSelect2RemoteOptions = function(elem) { ...becomes UserUnitPositionForm.prototype.getSelect2RemoteOptions = function(elem) {...) and use the promise returned by initUI, like this :

this.$UserUnitPositionForm.bsInitUI(this.options.styleInputs).then(function(){
    var $userRoleDefinition = $('.js-organizationUnitSelect');
    $userRoleDefinition.select2(this.getSelect2RemoteOptions($userRoleDefinition));
}.bind(this));
mstfcck commented 9 years ago

Thank you for helps. It worked! ;)

require([ 'jquery', 'jquery-ui', 'bootstrap', 'bforms-validate-unobtrusive', 'bforms-initUI', 'bforms-ajax', 'bforms-resetInput', 'bforms-extensions', 'bforms-inlineQuestion', 'bforms-namespace', 'bforms-select2', 'main-script' ], function () {

var UserUnitPositionForm = function (options) {
    this.options = $.extend(true, {}, options);
};

UserUnitPositionForm.prototype.getSelect2RemoteOptions = function (elem) {
    var $elem = $(elem),
        options = $elem.data('options');

    var s2ropts = {
        placeholder: $elem.attr('placeholder'),
        multiple: $elem.attr('multiple'),
        ajax: {
            url: this.options.createUrlGetUnitsByUserId, // Get unit bu userId: (public BsJsonResult GetUnitsByUserId(int page, string search, string userId))
            dataType: 'json',
            data: $.proxy(function (search, page) {
                return {
                    extraData: options.extraData,
                    search: search,
                    page: page,
                    userId: 1
                };
            }, this),
            results: function (response, page) {

                var data = response.Data,
                    results = [],
                    more = false;

                if (data) {

                    if (data.Items) {

                        more = (page * data.PageSize) < data.Count;

                        for (var i = 0; i < data.Items.length; i++) {
                            var item = data.Items[i];

                            results.push({
                                id: item.Value,
                                text: item.Text
                            });
                        }
                    }
                }

                return { results: results, more: more };
            },
            error: function (jqXHR, status, error) {
                return {
                    errorMessage: jqXHR.statusText
                };
            }
        },
        initSelection: function (element, callback) {
            if ($elem.data('init')) {
                var init = $elem.data('init');
                callback(init);
            }
        },
        escapeMarkup: function (m) { return m; }
    };

    return s2ropts;
};

UserUnitPositionForm.prototype.init = function () {
    this.$UserUnitPositionForm = $('.js-formCreate');

    //this.$UserUnitPositionForm.bsInitUI(this.options.styleInputs);
    this.$UserUnitPositionForm.bsInitUI(this.options.styleInputs).then(function () {
        var $UserUnitPositionForm = $('.js-organizationUnitSelect');
        $UserUnitPositionForm.select2(this.getSelect2RemoteOptions($UserUnitPositionForm));
    }.bind(this));

    this.addHandlers();
};

UserUnitPositionForm.prototype.addHandlers = function () {
    this.$UserUnitPositionForm.on('click', '.js-buttonCreate', $.proxy(this._onCreate, this));
};

UserUnitPositionForm.prototype._onCreate = function (e) {
   //...
};

$(document).ready(function () {
    var ctrl = new UserUnitPositionForm(requireConfig.pageOptions);
    ctrl.init();
});

});