3

I'm developing a simple blog application where users submitting a new post can also choose to associate a selection of predefined tags to their Post.

Currently, users can select multiple tags (holding ctrl) using a dropdownlist:

enter image description here

However, aesthetically this is not really what I want to use for this purpose and would prefer to let users select desired tags from a series of checkboxes, or even better - buttons. Similar to this:

enter image description here

Does such an equivalent HTML helper exist?

My working code with dropdownlist is below for your reference.

VIEW:

    <div class="form-group">
        @Html.DropDownList("TagId", (MultiSelectList)ViewBag.Tags, new { multiple = "multiple" })
    </div>

CONTROLLER:

_context is my database containing two tables, Post and Tags.

    public ActionResult Add()
    {

        var tags = _context.Tags.Select(c => new {
            TagID = c.TagID,
            Type = c.Type
        }).ToList();

        ViewBag.Tags= new MultiSelectList(tags, "TagID", "Type");

        return View();
    }

    [AcceptVerbs("POST")]
    public ActionResult Add()
    {

         string TagId= Request["TagID"];

         var tagids = TagId.Split(',');

         foreach (var tagid in tagids)
         {
               var id = int.Parse(tagid);
               Tag tag = _context.Tags.Where(ct => ct.TagId== id).First();
               post.Tags.Add(tag);
        }

        //Save our post
        _context.Post.Add(post);
        _context.SaveChanges();


        return View();
    }

Any alternative suggestion are also highly appreciated, particularly as I'm still learning the ropes - thanks!

Scribbio
  • 113
  • 10
  • 1
    No (at least in the MVC framework). But you can easily write you own `HtmlHelper` extension method, and I'm sure there would be a few plugins that you could find on the web, for example [CheckBoxList.Mvc](https://github.com/stephenzeng/CheckBoxList.Mvc) –  Dec 20 '17 at 22:18
  • 1
    As a side note, do not use `DropDownList()` to generate a multiple select - you need to use `ListBoxFor()` (refer [this answer](https://stackoverflow.com/questions/40725358/why-does-the-dropdownlistfor-lose-the-multiple-selection-after-submit-but-the-li/40732481#40732481) for an explanation) –  Dec 20 '17 at 22:22

1 Answers1

1

So as Stephen said, there is no baked in solution. Here is what I have done in the past (alternative version as requested in your question.) Use a class with select items to hold your possible values and a list of selected values for user interaction with checkboxes being used as your entry exit point into the selected list. then you can style your checkboxes as buttons and add a bit of jquery to make the btn-checkbox interaction occur. Example attached:

Model:

public class TagModel
{
    //well store our selection here
    public IList<string> SelectedTags { get; set; }
    //well load tags in here for selection
    public IList<SelectListItem> AvailableTags { get; set; }
    public TagModel()
    {
        SelectedTags = new List<string>();
        AvailableTags = new List<SelectListItem>();
    }
}

Controller:

    public ActionResult Index()
    {
        //load up available tags and pass them to view
        ViewBag.Tags = new TagModel { AvailableTags = GetTags() };
        return View();
    }

    [HttpPost]
    public ActionResult Index(TagModel mod)
    {
        if (ModelState.IsValid)
        {
            //mod.SelectedTags contains your data.... do something 
            return RedirectToAction("Weeeeeeeee");
        }
        ViewBag.Tags = new TagModel { AvailableTags = GetTags() };
        return View();
    }

Craptastic load function demo purpose:

    private IList<SelectListItem> GetTags()
    {
        return new List<SelectListItem>
    {
        new SelectListItem {Text = "cascading-style-sheet", Value = "1"},
        new SelectListItem {Text = "css-layout", Value = "2"},
        new SelectListItem {Text = "css-background-image", Value = "3"},
        new SelectListItem {Text = "css-attributes", Value = "4"},
    };
    }

View:

@using (Html.BeginForm("Index", "Home"))
{
    //get your tags from viewbag
    foreach (var item in ViewBag.Tags.AvailableTags)
    {
        //make them look like buttons by placing our bootstrap or whatever framework btn class on div above
        <div class="btn btn-danger chkBtn" >
                <input type="checkbox" class="checkNope" autocomplete="off" name="SelectedTags" value="@item.Value" /> @item.Text
         </div>
    }
    <div class="form-group text-center">
        <input type="submit" class="btn btn-primary" value="Submit" />
    </div>
}


@section scripts  { 
    <script>
        //when someone clicks a button then trigger the checkbox toggle and add/remove class toggle
        $('.chkBtn').on('click', function () {
            $(this).toggleClass('Weee');
            var checkbox = $(this).children('input[type="checkbox"]');
            checkbox.prop('checked', !checkbox.prop('checked'));
        })
    </script>
}

CSS Fun:

.checkNope {
    -webkit-appearance: button !important;
}


.Weee {
    background-color: #b5191f;
}
Travis Acton
  • 4,292
  • 2
  • 18
  • 30
  • Styling and iteration work a charm, thank you! However, the JS is not "associated" with any code and won't fire. Can you please double check that? – Scribbio Dec 21 '17 at 00:45
  • That’s a working example copied directly from a brand new MVC web project in VS2017. What error do you get in the console? All it is literally doing is actioning on the Btn click with a certain class. Did you move your script section in the your layout file? May want to wrap the script in a document.ready call. Are you using jquery (if not then you’ll have to port over some of that script back to plain JavaScript). – Travis Acton Dec 21 '17 at 03:23