Pergunta

I have an MVC ListBoxFor control that I'm trying to bind data to and update using a Kendo MultiSelectFor.

The idea being that there is a list of users in the ListBox, and a list of available users in the MultiSelect box. When users are selected from the MultiSelect box and the add button clicked, an Ajax call is made to an action that updates the users list server side (through various API calls, which all work fine) and client side JavaScript is used to update the users and available users array object and the binding keeps the controls up to date with the updated lists.

I wish I could pin this down to just one issue, but honestly every time I try something I come up with different errors, so I'll just go with the latest iteration.

Model:

public IEnumerable<UserInformation> Users { get; set; }
public IEnumerable<UserInformation> AvailableUsers { get; set; }

JavaScript ViewModel:

var viewModel = kendo.observable({
        availableUsersSelected: [],
        users: @(Html.Raw(Json.Encode(this.Model.Users))),
        availableUsers: @(Html.Raw(JsonConvert.SerializeObject(this.Model.AvailableUsers))),
        moveToUsers: function () {
            this.availableUsersSelected = this.get('availableUsersSelected');

            this.users.push(this.availableUsers);

            if (this.availableUsersSelected.length > 0) {
                var formAction = '@Url.Combine(Url.Content("~/"), ControllerActions.Groups.GroupDefault, ControllerActions.Groups.AddUser)';
                $.ajax({
                    url: formAction,
                    type: 'POST',
                    data: {
                        model: JSON.stringify(
                            {
                                groupId: $('#GroupId').val(),
                                users: this.availableUsersSelected
                            }
                    )
                    },
                    success: function (result) {
                        if (result) {
                            this.users.remove(this.availableUsersSelected);
                        }
                    }
                });
            }
        }
    });

MultiSelectFor control

@(Html.Kendo()
    .MultiSelectFor(u => u.AvailableUsers)
    .Placeholder("Please select")
    .BindTo(new SelectList(Model.AvailableUsers, "Id", "Name"))
    .HtmlAttributes(new { data_bind = "value: availableUsersSelected" })
)

ListBox control

@(Html.EditorLine(Language.Fields.Users, Html.ListBoxFor(u => u.Users, new SelectList(Model.Users, "Id", "Name"), new { @class = "form-control", data_bind = "source: users", data_value_field ="Id", data_text_field = "Name" })))

Add control

<img src="~/Content/images/up-arrow.jpg" alt="Move to users" width="30" data-bind="events: {click: moveToUsers}" />

To reiterate, the Ajax call and updating server side all work fine, it's the client side control binding that I'm struggling to understand.

The errors I'm getting are 1) a syntax error with the comma on this line users: @(Html.Raw(Json.Encode(this.Model.Users))), and the line after it (same thing, effectively), and 2) a "ReferenceError: Id is not defined" on the moveToUsers function call when the add button is pressed.

(I can honestly say that the amount of frustration I'm experiencing with this is driving me insane, so sorry if it came across in the question)

Foi útil?

Solução

So after calming down a bit, reading a few more bits of the documentation about data binding and observable arrays, I realised I was making a few fundamental errors.

JavaScript ViewModel:

var viewModel = {
        availableUsersSelected: new kendo.data.ObservableArray([]),
        users: new kendo.data.ObservableArray(@(Html.Raw(Json.Encode(this.Model.Users)))),
        availableUsers: new kendo.data.ObservableArray(@(Html.Raw(Json.Encode(this.Model.AvailableUsers)))),
        moveToUsers: function () {
            if (viewModel.availableUsersSelected.length > 0) {
                var formAction = '@Url.Combine(Url.Content("~/"), ControllerActions.Groups.GroupDefault, ControllerActions.Groups.AddUser)';
                $.ajax({
                    url: formAction,
                    type: 'POST',
                    data: {
                        model: JSON.stringify(
                            {
                                groupId: $('#GroupId').val(),
                                users: viewModel.availableUsersSelected
                            }
                    )
                    },
                    success: function (result) {
                        if (result) {
                            removeFromAvailableUsers();
                        }
                        else
                            alert('add failed!');
                    },
                    failure: function () {
                        alert('ajax failed!');
                    }
                });
            }
        }
    };

function removeFromAvailableUsers() {
    for (var i = 0; i < viewModel.availableUsersSelected.length; ++i) {
        viewModel.users.push(viewModel.availableUsersSelected[i]);
        viewModel.availableUsers.remove(viewModel.availableUsersSelected[i]);
    }
    var ele = $('#AvailableUsers').data("kendoMultiSelect");
    ele.value("");
    ele.input.blur();
};

The main differences are instead of declaring the entire object as a kendo observable are declaring each array as an observable array, then referencing them through the viewModel object instead of assuming that the "this" scope will encapsulate them.

Then, as D_Learning mentioned in the comments above, I was unnecessarily using two bindings for the MultiSelect control, so that then became:

    @(Html.Kendo()
        .MultiSelectFor(u => u.AvailableUsers)
        .Placeholder("Please select")
        .HtmlAttributes(new { data_bind = "source: availableUsers, value: availableUsersSelected", data_value_field = "Id", data_text_field = "Name" })
    )

(Notice no ".BindTo" property)

Aside from that, the MVC side of things stayed the same and it all words perfectly.

Outras dicas

If you wish to remove or add data to the Kendo Multiselect then you will need to add them via the DataSource as:

$("#AvailableUsers").data("kendoMultiSelect").dataSource.add({"text": "new Item", "value": 1000});

For more detail about Adding or removing Items to Multiselect (Kendo DataSrouce) see: Kendo DataSource Adding Removing Items

Similarly you can remove the item from the Listbox as below:

        var selectedIndex = ListBox1.selectedIndex();
        clearSelection();

        if (selectedIndex != -1) {
            ListBox1.options.remove(selectedIndex);

For more detail about Adding or removing Items from HTML Listbox see: HTML Listbox Items Manipulation.

Please let me know if you have any error after this.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top