MVC 5 ViewModel

I’m new to MVC, coming from Web Forms. I’m creating a small web site that basically display news. The news heading are loaded from the database and displayed in a view. That part works fine. When I click Read More I am taken to a new View that shows the
content of the news and images (if there are any). Now this is the part that troubles me. I can show the content fine, but the pictures come from a different database table, so that means I need to use a different model to show the images in my view.

So the question really is, what is the best way to combine two or more models so they can be used in a single view? I have tried using a ViewModel like so:

public class testViewModel
{
public List<news> allNews {get;set;}
public List<newsPictures> allPictures {get;set;}
}

Where news and newsPictures are my auto generated classes from the database.

In the view, I tried adding:

@model isvs2014.ModelViews.testViewModel

And then using:

@foreach (var x in Model.allNews)
{
}

but "allNews" is not there. So there must be something I am missing.

Any help is appreciated.

BlackRiver

And then using:

@foreach (var x in Model.allNews)
{
}

but "allNews" is not there. So there must be something I am missing.

Maybe forget to load allNews?!

That was just an example. I used <p>@html.DisplayFor(modelItem => x.newsBody)</p> inside the for loop. The problem is that allNews is not shown in the drop down list after I enter "Model.". I hope I explained it better :)

BlackRiver

The problem is that allNews is not shown in the drop down list after I enter "Model.". I hope I explained it better :)

re-compile.

Also, if does not show, put yourself and see if at runtime it gives an error( at compile time, only if you have MVC build views to true)

it totally depends how are you loading them, you need to show us your code how are you loading data in ViewModel.

Hi,

Please post your view code on controller and .cshtml.

Or you may look at this thread.

Have fun

Thank you jsiahaan,

The link you provided was very helpful. Still I struggle to understand one thing. If I have two domain models generated by EF and I want to create one ViewModel that represents let’s say a single "Review my orders" page, how would I populate my ViewModel
with the correct data?

For example, if I have a simple ShowOrderDetails controller that displays the details based on OrderID. How to I populate the ViewModel with the data corresponding to the provided OrderID?

That’s something I don’t understand.

public class myViewModel
{
   public string SomeProperty1 { get; set; }
   public string SomeProperty2 { get; set; }
   public string SomeProperty3 { get; set; }
}

Let’s say you want to populate SomeProperty1 and SomeProperty2 with data from your domain model 1, and SomeProperty3 with data from your domain model 2.

You can do something like:

public ActionResult ShowOrderDetails(int orderID=0) //Let's say we passed 5 as our orderID.
//The 0 here is for initialization since it's not nullable.
//To make it nullable, you can make it: int? orderID
{
myViewModel myvm = new myViewModel { SomeProperty1 = db.Domain1.Where(x=>x.OrderID==orderID) .Select(x=>x.SomeProperty1).Single(), SomeProperty2 = db.Domain1.Where(x=>x.OrderID==orderID) .Select(x=>x.SomeProperty2).Single(), SomeProperty3 = db.Domain2.Where(x=>x.OrderID==orderID) .Select(x=>x.SomeProperty3).Single() }; return View(myvm); }

In this example, we have LINQ queries to get the properties we want from our domain models based on the OrderID. Then we map the results of each query to each property of our viewmodel. Finally, we pass our viewmodel to the view.

I think you need to debug and check for the controller action method, that is responsible for returning the allNews. Please debug. I am pretty sure the values/data are not getting populated for the allNews from the controller itself.

Please post back your queries.

Thanks.

Jed0228, thank you very much for this. It was the example I needed to better understand the problem. Here’s how I solved my problem of displaying a news body with multiple pictures:

The ViewModel looks like this:

public class ReadMoreViewModels
    {
        public string newsBody { get; set; }
        public IEnumerable<Byte[]> picture { get; set; }

        public IEnumerable<string> pictureName { get; set; }

    }

Next, the ReadMore Action looks like this:

        public ActionResult ReadMore(int id=0)
        {
           
            isvsEntities newDB1 = new isvsEntities();
            ReadMoreViewModels myViewModel = new ReadMoreViewModels

            {
                newsBody = newDB1.news.Where(x=> x.newsID == id).Select(x=> x.newsBody).Single(),
                picture = newDB1.newsPictures.Where(x => x.newsID == id).Select(x => x.newsPictureData).AsEnumerable(),
                pictureName = newDB1.newsPictures.Where(x => x.newsID == id).Select(x => x.pictureCaption).AsEnumerable()
                
            };

            return View(myViewModel);


        }

Finally, the view is rendered like this:

@using isvs2014.Models 
@model isvs2014.Models.ReadMoreViewModels

@{
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<div class="row">
   
        <div class="col-md-10">

            <h3>@Html.DisplayFor(modelItem => Model.newsBody)</h3>
            <p>@Html.ActionLink("Nazad", "Index", null, new { @class = "btn btn-default" })</p>
           
        </div>

    <div class="col-md-10">
        @foreach (var item in Model.pictureName)
        {
            <p>@Html.DisplayFor(modelItem => item)</p>
        }
    </div>

    <div class="col-md-10">
        @foreach (var item1 in Model.picture)
        {
        <img src="data:image/png;base64,@(Convert.ToBase64String(item1))" width="100" height="100" />
        }
    </div>

    

</div>

Looking at the view, I am pretty sure it would be better with just one foreach statement, but I’m still a bit unsure how to accomplish that.

BlackRiver

    <div class="col-md-10">
        @foreach (var item in Model.pictureName)
        {
            <p>@Html.DisplayFor(modelItem => item)</p>
        }
    </div>

    <div class="col-md-10">
        @foreach (var item1 in Model.picture)
        {
        <img src="data:image/png;base64,@(Convert.ToBase64String(item1))" width="100" height="100" />
        }
    </div>

    

</div>

Looking at the view, I am pretty sure it would be better with just one foreach statement, but I’m still a bit unsure how to accomplish that.

This is already ok. You can’t combine them since you are looping thru 2 different IEnumerables. The items in "pictureName" are not the same as the items in "picture".

I see. But, what if I wanted to set pictureName as the Alt attribute in each picture? Do I need to modify my ViewModel somehow? Because I can’t see it happening in my current setup.

Thanks

As a follow up to my previous comment, here’s what I did next:

Changed the model:

    public class ReadMoreViewModels
    {
        public string newsBody { get; set; }
        public IEnumerable<newsPicture> pictures { get; set; }
    }

Changed the Action:

        public ActionResult ReadMore(int id=0)
        {
           
            isvsEntities newDB1 = new isvsEntities();
            ReadMoreViewModels myViewModel = new ReadMoreViewModels
            
            {
                newsBody = newDB1.news.Where(x=> x.newsID == id).Select(x=> x.newsBody).Single(),
                pictures = newDB1.newsPictures.Where(x => x.newsID == id).AsEnumerable()
            };
            
            return View(myViewModel);


        }

Modified the view:

<div class="row">
   
        <div class="col-md-10">

            <h3>@Html.DisplayFor(modelItem => Model.newsBody)</h3>
            <p>@Html.ActionLink("Nazad", "Index", null, new { @class = "btn btn-default" })</p>
           
        </div>

    <div class="col-md-10">
        @foreach (var item in Model.pictures)
        {
            <p>@Html.DisplayFor(modelItem => item.pictureCaption)</p>
            <img src="data:image/png;base64,@(Convert.ToBase64String(item.newsPictureData))" width="100" height="100" />
        }
    </div>
 

</div>

Works fine. A slight concern of mine: Am I doing some kind of security violation by exposing the entire newsPictures model in the controller? Or am I just not understanding it correctly?

BlackRiver

Works fine. A slight concern of mine: Am I doing some kind of security violation by exposing the entire newsPictures model in the controller? Or am I just not understanding it correctly?

If you mean mapping from your model to your viewmodel, then I guess not. Unless you include business logic in your viewmodel and expose it to your view
Tongue Out

Leave a Reply