I wanna pass to my "View" if there's any unreaded interaction within a call.
It's like an e-mail, If you have an unread email within a group of emails, it will be bold displayed. I'm doing something like that.
I have a Repository, so I'm doing this...
foreach(item in callRepo.All())
{
if (item.Interactions.Count(x=>x.Unread==true)>0)
{
}
}
return View(callRepo.All());
A call has many Interactions, and I wanna pass to my View if there is any unread Interaction.
This is how it's working on my View now:
var CSSclass="Readed"
foreach(item in callRepo.All())
{
if (item.Interactions.Count(x=>x.Unread==true)>0){CSSclass="Unreaded"}
<tr class=#CSSclass>}
I do not want to treat this business on my View.
Is there a way to do that on my controller?
.
Is my question clear? Sorry about the bad english.Tks
How many time are you going to call this repository method (I've counted 3 so far)?
I would recommend you to use a view model:
public class MyViewModel
{
public bool IsRead { get; set; }
public IEnumerable<Interaction> Interactions { get; set; }
}
then in your controller:
public ActionResult Index()
{
var items = callRepo.All();
var model = items.Select(item => new MyViewModel
{
Interactions = item.Interactions,
IsRead = item.Interactions.Any(x => x.Unread == true)
});
return View(model);
}
and your view:
#model IEnumerable<MyViewModel>
<table>
#Html.DisplayForModel()
</table>
and in the corresponding display template (~/Views/Shared/DisplayTemplates/MyViewModel.cshtml):
#model MyViewModel
#{
var css = model.IsRead ? "read" : "unread";
}
<tr class="#css">
...
</tr>
A further improvement of this code would be to replace the IEnumerable<Interaction> property in the view model by a specific IEnumerable<InteractionViewModel> and use AutoMapper to externalize the mapping between the model and the view model.
Related
This question and community wiki answer has been added to assist in closing out numerous unanswered questions as discussed in this meta post.
I have some code and when it executes, it throws an exception saying:
The model item passed into the dictionary is of type Bar but this dictionary requires a model item of type Foo
What does this mean, and how do I fix it?
The error means that you're navigating to a view whose model is declared as typeof Foo (by using #model Foo), but you actually passed it a model which is typeof Bar (note the term dictionary is used because a model is passed to the view via a ViewDataDictionary).
The error can be caused by
Passing the wrong model from a controller method to a view (or partial view)
Common examples include using a query that creates an anonymous object (or collection of anonymous objects) and passing it to the view
var model = db.Foos.Select(x => new
{
ID = x.ID,
Name = x.Name
};
return View(model); // passes an anonymous object to a view declared with #model Foo
or passing a collection of objects to a view that expect a single object
var model = db.Foos.Where(x => x.ID == id);
return View(model); // passes IEnumerable<Foo> to a view declared with #model Foo
The error can be easily identified at compile time by explicitly declaring the model type in the controller to match the model in the view rather than using var.
Passing the wrong model from a view to a partial view
Given the following model
public class Foo
{
public Bar MyBar { get; set; }
}
and a main view declared with #model Foo and a partial view declared with #model Bar, then
Foo model = db.Foos.Where(x => x.ID == id).Include(x => x.Bar).FirstOrDefault();
return View(model);
will return the correct model to the main view. However the exception will be thrown if the view includes
#Html.Partial("_Bar") // or #{ Html.RenderPartial("_Bar"); }
By default, the model passed to the partial view is the model declared in the main view and you need to use
#Html.Partial("_Bar", Model.MyBar) // or #{ Html.RenderPartial("_Bar", Model.MyBar); }
to pass the instance of Bar to the partial view. Note also that if the value of MyBar is null (has not been initialized), then by default Foo will be passed to the partial, in which case, it needs to be
#Html.Partial("_Bar", new Bar())
Declaring a model in a layout
If a layout file includes a model declaration, then all views that use that layout must declare the same model, or a model that derives from that model.
If you want to include the html for a separate model in a Layout, then in the Layout, use #Html.Action(...) to call a [ChildActionOnly] method initializes that model and returns a partial view for it.
This question already has a great answer, but I ran into the same error, in a different scenario: displaying a List in an EditorTemplate.
I have a model like this:
public class Foo
{
public string FooName { get; set; }
public List<Bar> Bars { get; set; }
}
public class Bar
{
public string BarName { get; set; }
}
And this is my main view:
#model Foo
#Html.TextBoxFor(m => m.Name, new { #class = "form-control" })
#Html.EditorFor(m => m.Bars)
And this is my Bar EditorTemplate (Bar.cshtml)
#model List<Bar>
<div class="some-style">
#foreach (var item in Model)
{
<label>#item.BarName</label>
}
</div>
And I got this error:
The model item passed into the dictionary is of type 'Bar', but this
dictionary requires a model item of type
'System.Collections.Generic.List`1[Bar]
The reason for this error is that EditorFor already iterates the List for you, so if you pass a collection to it, it would display the editor template once for each item in the collection.
This is how I fixed this problem:
Brought the styles outside of the editor template, and into the main view:
#model Foo
#Html.TextBoxFor(m => m.Name, new { #class = "form-control" })
<div class="some-style">
#Html.EditorFor(m => m.Bars)
</div>
And changed the EditorTemplate (Bar.cshtml) to this:
#model Bar
<label>#Model.BarName</label>
Observe if the view has the model required:
View
#model IEnumerable<WFAccess.Models.ViewModels.SiteViewModel>
<div class="row">
<table class="table table-striped table-hover table-width-custom">
<thead>
<tr>
....
Controller
[HttpGet]
public ActionResult ListItems()
{
SiteStore site = new SiteStore();
site.GetSites();
IEnumerable<SiteViewModel> sites =
site.SitesList.Select(s => new SiteViewModel
{
Id = s.Id,
Type = s.Type
});
return PartialView("_ListItems", sites);
}
In my case I Use a partial view but runs in normal views
Consider the partial map.cshtml at Partials/Map.cshtml. This can be called from the Page where the partial is to be rendered, simply by using the <partial> tag:
<partial name="Partials/Map" model="new Pages.Partials.MapModel()" />
This is one of the easiest methods I encountered (although I am using razor pages, I am sure same is for MVC too)
First you need to return an IEnumerable version of your model to the list view.
#model IEnumerable<IdentityManager.Models.MerchantDetail>
Second, you need to return a list from the database. I am doing it via SQL Server, so this is code I got working.
public IActionResult Merchant_Boarding_List()
List<MerchantDetail> merchList = new List<MerchantDetail>();
var model = new MerchantDetail();
try
{
using (var con = new SqlConnection(Common.DB_CONNECTION_STRING_BOARDING))
{
con.Open();
using (var command = new SqlCommand("select * from MerchantDetail md where md.UserGUID = '" + UserGUID + "'", con))
{
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
model.biz_dbaBusinessName = reader["biz_dbaBusinessName"].ToString();
merchList.Add(model);
}
}
}
}
}
catch (Exception ex)
{
}
return View(merchList);
Passing the model value that is populated from a controller method to a view
public async Task<IActionResult> Index()
{
//Getting Data from Database
var model= await _context.GetData();
//Selecting Populated Data from the Model and passing to view
return View(model.Value);
}
one more thing.
if your view is a partial/sub page and the model for that partial view is null for some reason (e.g no data) you will get this error. Just need to handle the null partial view model
Ok, I have read a bunch of articles, and I am still lost, so I figure I will put the question out here.
I am trying to create a dynamic dropdown in my "posts" create view. I would like to pull the selectList items from my Categories.sdf, which has a table called categories and two columns, "CategoryID" and "CategoryTitle".
I know I need to pull the items into the viewbag within by "postscontroller" so they can be passed to the view. But I am not sure what this would look like. Again, i'm new to MVC so if i sound like a dope, i apologize.
I know I need to pull the items into the viewbag within by "postscontroller"
Oh no, you don't need to do anything like that.
You could start by defining a view model:
public class PostViewModel
{
[DisplayName("Select a category")]
[Required]
public string SelectedCategoryId { get; set; }
public IEnumerable<SelectListItem> Categories { get; set; }
}
that you will populate in your controller:
public class PostsController: Controller
{
public ActionResult Index()
{
var model = new PostViewModel();
model.Categories = db.Categories.ToList().Select(c => new SelectListItem
{
Value = c.CategoryId,
Text = c.CategoryName
});
return View(model);
}
}
and then have a corresponding strongly typed view (~/views/posts/index.cshtml):
#model PostViewModel
#using (Html.BeginForm())
{
#Html.LabelFor(x => x.SelectedCategoryId)
#Html.DropDownListFor(x => x.SelectedCategoryId, Model.Categories, "-- select --")
#Html.ValidationMessageFor(x => x.SelectedCategoryId)
<button type="submit">OK</button>
}
I am using MVC3. I'm binding the dropdown with the Data coming from a service. But after the page posts back and a filter applies to list, the dropdown shows the filter record value in the grid because I always bind the list coming from the service.
However, I want the dropdown to always show all the Records in the database.
I don't understand your question that clearly. But it seems that it is a dropdown that you have on your view? I also have no idea what you are trying to bind so I created my own, but have a look at my code and modify it to fit in with your scenario.
In your view:
#model YourProject.ViewModels.YourViewModel
On the view there is a list of banks in a dropdown list.
Your banks dropdown:
<td><b>Bank:</b></td>
<td>
#Html.DropDownListFor(
x => x.BankId,
new SelectList(Model.Banks, "Id", "Name", Model.BankId),
"-- Select --"
)
#Html.ValidationMessageFor(x => x.BankId)
</td>
Your view model that will be returned to the view:
public class YourViewModel
{
// Partial class
public int BankId { get; set; }
public IEnumerable<Bank> Banks { get; set; }
}
Your create action method:
public ActionResult Create()
{
YourViewModel viewModel = new YourViewModel
{
// Get all the banks from the database
Banks = bankService.FindAll().Where(x => x.IsActive)
}
// Return the view model to the view
// Always use a view model for your data
return View(viewModel);
}
[HttpPost]
public ActionResult Create(YourViewModel viewModel)
{
if (!ModelState.IsValid)
{
// If there is an error, rebind the dropdown.
// The item that was selected will still be there.
viewModel.Banks = bankService.FindAll().Where(x => x.IsActive);
return View(viewModel);
}
// If you browse the values of viewModel you will see that BankId will have the
// value (unique identifier of bank) already set. Now that you have this value
// you can do with it whatever you like.
}
Your bank class:
public class Bank
{
public int Id { get; set; }
public string Name { get; set; }
public bool IsActive { get; set; }
}
This is simple as it gets. I hope this helps :)
PS: Please remember with future posts, always give as much detail as possible so that we can help you better. Also don't forget to display code samples so that we can see what you have already done. The more details that we can have the better.
When you post the model back to the create[Http Post] action is it not possible to have the list of dropdown list values for the banks binded back to the model. I see that if the model is invalid, you call the code
viewModel.Banks = bankService.FindAll().Where(x => x.IsActive);
to get a list of all the banks again which I assume you need to hit the database again.
Thanks
Can I return List to my View from my Controller, or do I need to wrap my List in another class, just for the sake of returning only a class?
If this is possible, how do I iterate over the list? I'm trying this (Razor), but Visual Studio isn't happy about it:
#model List<QueueItem>
.
.
.
#foreach (item in #Model)
You can do it that way, but I never return my domain objects directly to a view. Normally I have a view model for a view containing only the fields that I need on that view.
Given your scenario I would have this list in a view model:
public class MyViewModel
{
public IEnumerable<QueueItem> QueueItems { get; set; }
}
In your action method you will populate these items from a service/repository call and then this view model would be returned to the view:
public ActionResult List()
{
MyViewModel viewModel = new MyViewModel
{
QueueItems = queueItemRepository.GetAllQueueItems()
};
return View(viewModel);
}
In your view you would loop through the items and do what needs to be done:
#foreach (var item in Model.QueueItems)
{
}
I hope this helps.
I have 2 models:
public class Person
{
public int PersonID { get; set; }
public string PersonName { get; set; }
}
public class Order
{
public int OrderID { get; set; }
public int TotalSum { get; set; }
}
I want edit objects of BOTH classes in SINGLE view, so I need something like:
#model _try2models.Models.Person
#model _try2models.Models.Order
#using(Html.BeginForm())
{
#Html.EditorFor(x => x.PersonID)
#Html.EditorFor(x => x.PersonName)
#Html.EditorFor(x=>x.OrderID)
#Html.EditorFor(x => x.TotalSum)
}
This, of course, don't work: Only one 'model' statement is allowed in a .cshtml file. May be there is some workaround?
Create a parent view model that contains both models.
public class MainPageModel{
public Model1 Model1{get; set;}
public Model2 Model2{get; set;}
}
This way you can add additional models at a later date with very minimum effort.
To use the tuple you need to do the following, in the view change the model to:
#model Tuple<Person,Order>
to use #html methods you need to do the following i.e:
#Html.DisplayNameFor(tuple => tuple.Item1.PersonId)
or
#Html.ActionLink("Edit", "Edit", new { id=Model.Item1.Id }) |
Item1 indicates the first parameter passed to the Tuple method and you can use Item2 to access the second model and so on.
in your controller you need to create a variable of type Tuple and then pass it to the view:
public ActionResult Details(int id = 0)
{
Person person = db.Persons.Find(id);
if (person == null)
{
return HttpNotFound();
}
var tuple = new Tuple<Person, Order>(person,new Order());
return View(tuple);
}
Another example : Multiple models in a view
Another option which doesn't have the need to create a custom Model is to use a Tuple<>.
#model Tuple<Person,Order>
It's not as clean as creating a new class which contains both, as per Andi's answer, but it is viable.
If you are a fan of having very flat models, just to support the view, you should create a model specific to this particular view...
public class EditViewModel
public int PersonID { get; set; }
public string PersonName { get; set; }
public int OrderID { get; set; }
public int TotalSum { get; set; }
}
Many people use AutoMapper to map from their domain objects to their flat views.
The idea of the view model is that it just supports the view - nothing else. You have one per view to ensure that it only contains what is required for that view - not loads of properties that you want for other views.
ok, everyone is making sense and I took all the pieces and put them here to help newbies like myself that need beginning to end explanation.
You make your big class that holds 2 classes, as per #Andrew's answer.
public class teamBoards{
public Boards Boards{get; set;}
public Team Team{get; set;}
}
Then in your controller you fill the 2 models. Sometimes you only need to fill one. Then in the return, you reference the big model and it will take the 2 inside with it to the View.
TeamBoards teamBoards = new TeamBoards();
teamBoards.Boards = (from b in db.Boards
where b.TeamId == id
select b).ToList();
teamBoards.Team = (from t in db.Teams
where t.TeamId == id
select t).FirstOrDefault();
return View(teamBoards);
At the top of the View
#model yourNamespace.Models.teamBoards
Then load your inputs or displays referencing the big Models contents:
#Html.EditorFor(m => Model.Board.yourField)
#Html.ValidationMessageFor(m => Model.Board.yourField, "", new { #class = "text-danger-yellow" })
#Html.EditorFor(m => Model.Team.yourField)
#Html.ValidationMessageFor(m => Model.Team.yourField, "", new { #class = "text-danger-yellow" })
And. . . .back at the ranch, when the Post comes in, reference the Big Class:
public ActionResult ContactNewspaper(teamBoards teamboards)
and make use of what the model(s) returned:
string yourVariable = teamboards.Team.yourField;
Probably have some DataAnnotation Validation stuff in the class and probably put if(ModelState.IsValid) at the top of the save/edit block. . .
In fact there is a way to use two or more models on one view without wrapping them in a class that contains both.
Using Employee as an example model:
#model Employee
Is actually treated like.
#{ var Model = ViewBag.model as Employee; }
So the View(employee) method is setting your model to the ViewBag and then the ViewEngine is casting it.
This means that,
ViewBag.departments = GetListOfDepartments();
return View(employee);
Can be used like,
#model Employee
#{
var DepartmentModel = ViewBag.departments as List<Department>;
}
Essentially, you can use whatever is in your ViewBag as a "Model" because that's how it works anyway. I'm not saying that this is architecturally ideal, but it is possible.
Just create a single view Model with all the needed information in it, normaly what I do is create a model for every view so I can be specific on every view, either that or make a parent model and inherit it. OR make a model which includes both the views.
Personally I would just add them into one model but thats the way I do it:
public class xViewModel
{
public int PersonID { get; set; }
public string PersonName { get; set; }
public int OrderID { get; set; }
public int TotalSum { get; set; }
}
#model project.Models.Home.xViewModel
#using(Html.BeginForm())
{
#Html.EditorFor(x => x.PersonID)
#Html.EditorFor(x => x.PersonName)
#Html.EditorFor(x => x.OrderID)
#Html.EditorFor(x => x.TotalSum)
}
You can use the presentation pattern http://martinfowler.com/eaaDev/PresentationModel.html
This presentation "View" model can contain both Person and Order, this new
class can be the model your view references.
Another way that is never talked about is
Create a view in MSSQL with all the data you want to present. Then use LINQ to SQL or whatever to map it. In your controller return it to the view. Done.
you can't declare two model on one view, try to use Html.Action("Person", "[YourController]") & Html.Action("Order", "[YourController]").
Good luck.
Beside of one view model in asp.net you can also make multiple partial views and assign different model view to every view, for example:
#{
Layout = null;
}
#model Person;
<input type="text" asp-for="PersonID" />
<input type="text" asp-for="PersonName" />
then another partial view Model for order model
#{
Layout = null;
}
#model Order;
<input type="text" asp-for="OrderID" />
<input type="text" asp-for="TotalSum" />
then in your main view load both partial view by
<partial name="PersonPartialView" />
<partial name="OrderPartialView" />
I hope you find it helpfull !!
i use ViewBag For Project and Model for task so in this way i am using two model in single view and in controller i defined viewbag's value or data
List<tblproject> Plist = new List<tblproject>();
Plist = ps.getmanagerproject(c, id);
ViewBag.projectList = Plist.Select(x => new SelectListItem
{
Value = x.ProjectId.ToString(),
Text = x.Title
});
and in view tbltask and projectlist are my two diff models
#{
IEnumerable<SelectListItem> plist = ViewBag.projectList;
}
#model List