0

I'm having some troubles with Models and ViewModels.

I need list all bills and the users in the same view. (users in a dropdown list)

Also: When to use IEnumerable<T>? Because depending I change the view change the error message.

Model

public class Bill
{
    public int Id { get; set; }
    public string Title { get; set; }
    public DateTime Date { get; set; }
    public string Category { get; set; }
    public double Amount { get; set; }

    public Card Card { get; set; }
    public int CardId { get; set; }
}

ViewModel

public class UserBills
{
    public IEnumerable<ApplicationUser> User { get; set; }
    public Bill Bill { get; set; }
}

View

@*@model IEnumerable<Nucontrol.Models.Bill>*@
@model IEnumerable<Nucontrol.ViewModels.UserBills>

@foreach (var item in Model)
{
    <tr>
        <td>
            @Html.DisplayFor(modelItem => item.Bill.Card.Number)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Bill.Title)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Bill.Date)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Bill.Category)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Bill.Amount)
        </td>
        <td>
            @Html.ActionLink("Edit", "Edit", new { id = item.Bill.Id }) |
            @Html.ActionLink("Details", "Details", new { id = item.Bill.Id }) |
            @Html.ActionLink("Split", "Split",  new { id = item.Bill.Id }, new { data_target = "#myModal", data_toggle = "modal" })
        </td>
    </tr>
}

<!-- List all users -->
@Html.DropDownListFor(m => User.Identity.Name, new SelectList(User.Identity.Name, "Id", "Name"), "", new { @class = "form-control" })

Controller

public ActionResult Index()
{
    var users = _context.Users.ToList();
    var bills = _context.Bills.ToList();

    var viewModel = new UserBills
    {
        User = users
    };

    return View(viewModel);
}
wbail
  • 536
  • 1
  • 7
  • 18
  • http://www.c-sharpcorner.com/article/different-ways-bind-the-value-to-razor-dropdownlist-in-aspnet-mvc5/ – Chetan Feb 14 '18 at 00:35
  • Can you add your Bill and ApplicationUser Models. Are you trying to show all Bills for a single user based on the selected user in the drop down? Can you explain what you are trying to achieve a bit more. – enifeder Feb 14 '18 at 00:54
  • I think this DDLF doesn't make sense: `@Html.DropDownListFor(m => User.Identity.Name, new SelectList(User.Identity.Name, "Id", "Name"), "", new { @class = "form-control" })`. You need to use `IEnumerable` inside viewmodel class to build `SelectList` from that. Also often `User.Identity.Name` set from the cookie or user login instead of DDLF selection. – Tetsuya Yamamoto Feb 14 '18 at 01:33
  • I need list all bills and all users in the same view – wbail Feb 14 '18 at 01:40
  • You can use `UserManager` and `User = userManager.Users.ToList()` to build all user names list, then use `new SelectList(Model.User)` in DDLF context. Also `IIdentity.Name` is getter property, you can only obtain its value but can't set it directly without setting `HttpContext.User` first. – Tetsuya Yamamoto Feb 14 '18 at 02:01

1 Answers1

1

You have some issues in sample code provided:

1) The DropDownListFor uses User.Identity.Name property as model binding, which actually derived from IIdentity.Name which is getter-only property. Declare another property with setter available which holds user ID in your viewmodel.

2) Passing UserBills viewmodel into view which bound to IEnumerable<UserBills> model may cause InvalidOperationException. You need to use either passing IEnumerable<UserBills> from controller or define @model UserBills.

3) I suggest you use IEnumerable<SelectListItem> to create DropDownListFor items from IEnumerable<ApplicationUser> generated by identity data context and pass it to view (see also IdentityUser properties).

Here is initial solution based from my thought:

Model

public class UserBills
{
    public int UserId { get; set; }
    public IEnumerable<SelectListItem> Users { get; set; }
    public IEnumerable<Bill> Bills { get; set; }
}

Controller

public ActionResult Index()
{
    var users = _context.Users.ToList();
    var bills = _context.Bills.ToList();
    var viewModel = new UserBills
    {
        Users = users.Select(x => new SelectListItem() { Value = x.Id.ToString(), Text = x.UserName.ToString() }),
        Bills = bills
    }

    return View(viewModel);    
}

View

@model Nucontrol.ViewModels.UserBills

@foreach (var item in Model.Bills)
{
    <tr>
        <td>
            @Html.DisplayFor(modelItem => item.Card.Number)
        </td>
        <!-- other properties -->
    </tr>
}

@Html.DropDownListFor(m => m.UserId, Model.Users, "", new { @class = "form-control" })

NB: Since you're getting selected user ID from viewmodel binding, it is possible to create HttpContext.User instance and setting User.Identity.Name property from that ID.

Tetsuya Yamamoto
  • 24,297
  • 8
  • 39
  • 61