ASP.NET CORE MVC select tag helper - how to set selected value without using ModelView

nam picture nam · Sep 22, 2016 · Viewed 13.3k times · Source

In the following code of an ASP.NET MVC Core project, the Get action method Test(...) is displaying a dropdown list of years. By default, the dropdown displays the first year in the list (i.e. 2000) as a Selected value. If I want to display a particular year in the same list as a selected value, how can I achieve it from action method Test(...) without using ViewModel techinque? A solution for either a select tag helper or a DropdownList html helper will be fine - but Selected value has to set from action method.

In short, how would you set a particular year as a Selected Value in this list: ViewBag.YearsList = Enumerable.Range(2000, 15).Select(g => new SelectListItem { Value = g.ToString(), Text = g.ToString() }).ToList();

Models:

    public class BloggingContext : DbContext
    {
        public BloggingContext(DbContextOptions<BloggingContext> options)
            : base(options)
        { }

        public DbSet<Blog> Blogs { get; set; }
        public DbSet<Post> Posts { get; set; }
    }

    public class Blog
    {
        public int BlogId { get; set; }
        public string Url { get; set; }

        public IList<Post> Posts { get; set; }
    }

    public class Post
    {
        public int PostId { get; set; }
        public string Title { get; set; }
        public string Content { get; set; }
        public int PostYear { get; set; }
        public int BlogId { get; set; }
        public Blog Blog { get; set; }
}

Controller: Only relevant action methods are shown.

 public class BlogsController : Controller
{
    private readonly BloggingContext _context;

    public BlogsController(BloggingContext context)
    {
        _context = context;    
    }

    // GET: Blogs
    public async Task<IActionResult> Index()
    {
        return View(_context.Blogs.ToList());
    }

    // GET: /Blogs/Test
    [HttpGet]
    public async Task<IActionResult> Test(string returnUrl = null)
    {
        ViewData["ReturnUrl"] = returnUrl;
        ViewBag.YearsList = Enumerable.Range(2000, 15).Select(g => new SelectListItem { Value = g.ToString(), Text = g.ToString() }).ToList();

        //return View(await _context.Blogs.Include(p => p.Posts).ToListAsync());
        var qrVM = from b in _context.Blogs
                    join p in _context.Posts on b.BlogId equals p.BlogId into bp
                    from c in bp.DefaultIfEmpty()
                    select new BlogsWithRelatedPostsViewModel { BlogID = b.BlogId, PostID = (c == null ? 0 : c.PostId), Url = b.Url, Title = (c == null ? string.Empty : c.Title), Content = (c == null ? string.Empty : c.Content) };

        return View(await qrVM.ToListAsync());
    }

    // POST: /Blogs/Test
    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Test(IList<BlogsWithRelatedPostsViewModel> model, string GO, int currentlySelectedIndex, string returnUrl = null)
    {
        ViewData["ReturnUrl"] = returnUrl;
        ViewBag.YearsList = Enumerable.Range(2000, 15).Select(g => new SelectListItem { Value = g.ToString(), Text = g.ToString() }).ToList();

        if (!string.IsNullOrEmpty(GO))
        {
            var qrVM = from b in _context.Blogs
                        join p in _context.Posts on b.BlogId equals p.BlogId into bp
                        from c in bp.DefaultIfEmpty()
                        where c == null? true : c.PostYear.Equals(currentlySelectedIndex)
                        select new BlogsWithRelatedPostsViewModel { BlogID = b.BlogId, PostID = (c == null ? 0 : c.PostId), Url = b.Url, Title = (c == null ? string.Empty : c.Title), Content = (c == null ? string.Empty : c.Content) };
            return View(await qrVM.ToListAsync());
        }
        else if (ModelState.IsValid)
        {
            foreach (var item in model)
            {
                var oPost = _context.Posts.Where(r => r.PostId.Equals(item.PostID)).FirstOrDefault();
                if (oPost != null)
                {
                    oPost.Title = item.Title;
                    oPost.Content = item.Content;
                    oPost.PostYear = currentlySelectedIndex;
                    oPost.BlogId = item.BlogID; //according to new post below the blogId should exist for a newly created port - but just in case
                }
                else
                {
                    if (item.PostID == 0)
                    {
                        Post oPostNew = new Post { BlogId = item.BlogID, Title = item.Title, Content = item.Content, PostYear = currentlySelectedIndex }; //need to use currentlySelectedIndex intead of item.FiscalYear in case of adding a record
                        _context.Add(oPostNew);
                    }

                }
            }
            await _context.SaveChangesAsync();
            //return RedirectToLocal(returnUrl);
            return View(model);
        }

        // If we got this far, something failed, redisplay form
        return View();
    }
 }

Test.cshtml View

@model IList<ASP_Core_Blogs.Models.BlogPostViewModels.BlogsWithRelatedPostsViewModel>

<div class="row">
    <div class="col-md-12">
        <form asp-controller="Blogs" asp-action="Test" asp-route-returnurl="@ViewData["ReturnUrl"]" method="post">
            @{
                IEnumerable<SelectListItem> yearsList = (IEnumerable<SelectListItem>)ViewBag.YearsList;
                var currentlySelectedIndex = 0; // Currently selected index (usually will come from model)
            }
            <strong>Select a Post Year</strong>
            <h6>Choose a year and a URL to begin:</h6>
            <label>Year:</label><select asp-for="@currentlySelectedIndex" asp-items="yearsList"></select><input type="submit" class="btn btn-default" name="GO" value="GO" />
            <table class="table">
                <thead>
                    <tr>
                        <th></th>
                        <th></th>
                        <th>Url</th>
                        <th>Title</th>
                        <th>Content</th>
                    </tr>
                </thead>
                <tbody>
                    @for (int i=0; i< Model.Count(); i++)
                    {
                        <tr>
                            <td>@Html.HiddenFor(r => r[i].BlogID)</td>
                            <td>@Html.HiddenFor(r => r[i].PostID)</td>
                            <td>
                                @Html.TextBoxFor(r => r[i].Url)
                            </td>
                            <td>
                                @Html.TextBoxFor(r => r[i].Title)
                            </td>
                            <td>
                                @Html.TextBoxFor(r => r[i].Content)
                            </td>
                        </tr>
                    }
                </tbody>
            </table>
            <button type="submit" class="btn btn-default">Save</button>
        </form>
    </div>
</div>

Answer

Khyron picture Khyron · Sep 23, 2016

Since you want to avoid using a view model, you can't use DropDownListFor but you can use DropDownList

    @Html.DropDownList("fieldname", new SelectList(ViewBag.YearsList, ViewBag.SelectedYear))