Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Data binding in MVC 5 and Select2 Multiple Values with Razor engine

Usually I do very little work on html side of the application because for the most part I just let that get generated for me.

I am working on an app for a blog with Posts Tags and Comments. What I want to do is when creating a new post, I should be able to add existing tags to the new post. I am trying to use Select2 but I can't figure out how to make the selected values passed to my Create method in the post controller so that they can be stored in the database.

Here is what I am working with:

namespace Blog.Data.Entities
{
    public class Post
    {
        public virtual long PostId { get; set; }

        -------------------

        public virtual ICollection<Tag> Tags { get; set; }
    }

    public class Tag
    {
        public virtual long TagId { get; set; }
        public virtual string Name { get; set; }
        public virtual string Description { get; set; }
    }
}

Post Controller

// POST: /Post/Create
    [HttpPost]
    public ActionResult Create(PostsCreateViewModel postModel)
    {
        if (ModelState.IsValid)
        {
            Post post = new Post
            {
                Title = postModel.Title,
                Body = postModel.Body,
                PostDate = _dateTime.UtcNow
            };

            foreach (var tag in postModel.Tags)
            {
                post.Tags.Add(_tagRepository.GetTag(tag.TagId));
            }

            _postRepository.SavePost(post);

            return RedirectToAction("Detail");
        }
        return View(postModel);
    }

I am successfully able to load data from remote with: Json code left out

<script type="text/javascript">
    $(document).ready(function () {
        $("#tags").select2(
        {
            placeholder: "Select a Tag",
            minimumInputLength: 1,
            multiple: true,
            maximumSelectionSize: 5,
            ajax: {
                url: '@Url.Action("SearchTag", "Post")',
                dataType: 'json',
                data: function (term, page) {
                    return {
                        searchTerm: term,
                        page_limit: 10,
                        page: page,
                    };
                },
                results: function (data, page) {
                    var more = (page * 10) < data.total;
                    return { results: data, more: more };
                }
            }
        });
    });
</script>

View: usually I would have something like

<div class="form-group">
            @Html.LabelFor(model => model.Title, new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.Title)
                @Html.ValidationMessageFor(model => model.Title)
            </div>
        </div>

How can I write similar html for my tag textbox, so that when I click save everything is saved to appropriate tables?

Currently I just have this for select2:

<div class="form-group">
    @Html.LabelFor(model => model.Tags, new { @class = "control-label col-md-2" })
    <div class="col-md-10">
        <input id="tags" style="width: 300px" />
        @Html.ValidationMessageFor(model => model.Tags)
    </div>
</div>

Which produces;

enter image description here

like image 839
Komengem Avatar asked May 01 '14 22:05

Komengem


3 Answers

I'm also using select2 and this works for me:

The View

@Html.ListBoxFor(M => M.TagsId, new MultiSelectList(Model.Tags, "Id", "Description"), new { @class = "form-control select-multiple", multiple = "multiple" })

The ViewModel

 public List<Tag> Tags { get; set; } = new List<Tag>();
 public int[] TagsId { get; set; }

You can use the Tags collection to fill the options in the multiple select and the TagsId array will be populated with the id of the selected tags. If you're populating the select options via ajax I guess you could ommit the Tags property.

Note I'm using a multiple input select and not a textbox.

like image 199
Roger Saladrigas Avatar answered Sep 19 '22 11:09

Roger Saladrigas


If the list and the selected items comes form different sources:

Example:

-Model.CategoryList >> the actual selectList items

-Model.SubCategories (m => m.SubCategories) >> the selected items only (Ids int[])

@Html.DropDownListFor(m => m.SubCategories, Model.CategoryList, Html.DescriptionFor(m => m.SubCategories), new { @class = "form-control m-select2 select2", multiple = "multiple" })

I did it like this with little js code:

@using Newtonsoft.Json;


<script>
    //Edit mode
    //set "selected" prop for the <select> manaually
    $(function () {
        var SubCategories_selected = @JsonConvert.SerializeObject(Model.SubCategories);
        SubCategories_selected.forEach(function (value, index) {
            $('[name="SubCategories"]').find(`[value="${value}"]`).prop("selected", true);
        });
        //recall the select2 init
        $('#SubCategories').select2();
    });
</script>
like image 43
Adel Mourad Avatar answered Sep 20 '22 11:09

Adel Mourad


i think it just same as a <select multiple>
for example

    <select name="abc" multiple>
        <option value=1>a</option>
        <option value=2>b</option>
        <option value=3>c</option>
        <option value=4>d</option>
    </select>

you choose option a and option b then submit the form with post method
the post data will be
abc 1
abc 2
and you can get the data in mvc action with a param like IEnumeralbe<int> abc

the action will like

public ActionResult ActionName(IEnumerable<int> abc)  

the select2 plugin only change the view,post data also use a http post

like image 33
chenZ Avatar answered Sep 17 '22 11:09

chenZ