MVC 3 using #model twice - asp.net-mvc

I would like to using two times #model to get data from another part of my website is it possible? Because now I have got error but if I have only this first #model everything working correct.
Look -> MVC 3 - Exception Details: System.InvalidOperationException
Error 2 'SportsStore.Entities.Kategorie' does not contain a definition for 'Opis' and no extension method 'Opis' accepting a first argument of type 'SportsStore.Entities.Kategorie' could be found (are you missing a using directive or an assembly reference?) c:\Users\Rafal\Desktop\MVC ksiązka\moj projekt\sklep\SportsStore.WebUI\Views\Product\List.cshtml 16 4 SportsStore.WebUI
#model IEnumerable<SportsStore.Entities.Towar>
#model IEnumerable<SportsStore.Entities.Kategorie>
#{
ViewBag.Title = "List";
}
<h2>List</h2>
#foreach (var p in Model)
{
<div class="item">
<h3>#p.Nazwa</h3>
#p.Opis
<h4>#p.Cena.ToString("c")</h4>
</div>
}

You only can have one Model per View. But you can use another object to declarate the model:
public class SomeViewModel
{
public IEnumerable<Towar> Towars;
public IEnumerable<Category> Categories;
public SomeViewModel(IEnumerable<Towar> towars, IEnumerable<Category> categories) {
Towars = towars;
Categories = categories;
}
}
And then use it in your view like this:
#model SportsStore.Entities.SomeViewModel
#foreach (var item in Model.Towars)
{
<div class="item">
<h3>#p.Nazwa</h3>
#p.Opis
<h4>#p.Cena.ToString("c")</h4>
</div>
}
#foreach (var item in Model.Categories) {
#item.Name #* or what you need down here *#
}
I would also recommend you to use english names in MVC. It's more clear to read and understand ;).

I think this would be a case to create a ViewModel (to combine the two entities you have) and then base a View off that ViewModel.

It is best to create a view model to represent your data. Your view model must only contain what you need to display on your view. Anything that is not used you can remove, no point of it being there. Your view model can look like this:
using SportsStore.Entities;
public class YourViewModel
{
public IEnumerable<Towar> Towars { get; set; }
public IEnumerable<Kategorie> Categories { get; set; } // I assume this is categories
}
Lets say that you have to use this view model in a create view then your create action can look something like this:
public class YourController : Controller
{
private readonly ITowarRepository towarRepository;
private readonly ICategoryRepository categoryRepository;
public YourController(ITowarRepository towarRepository, ICategoryRepository categoryRepository)
{
this.towarRepository = towarRepository;
this.categoryRepository = categoryRepository;
}
public ActionResult Create()
{
YourViewModel viewModel = new YourViewModel
{
Towars = towarRepository.GetAll(),
Categories = categoryRepository.GetAll()
};
return View(viewModel);
}
}
And then in your view:
#model YourProject.DomainModel.ViewModels.YourViewModel
#foreach (var towar in Model.Towars)
{
// Do whatever
}
#foreach (var category in Model.Categories)
{
// Do whatever
}

Related

How to pass 3 models data to asp.net MVC view [duplicate]

I am trying to generate such HTML
<form action="/some/process" method="post">
<input type="hidden" name="foo.a" value="aaa"/>
<input type="hidden" name="bar.b" value="bbb"/>
<input type="submit" />
</form>
so it can be processed by this Action:
public ActionResult Process(Foo foo, Bar bar)
{
...
}
Given the Action code
public ActionResult Edit()
{
ViewData["foo"] = new Foo { A = "aaa" };
ViewData["bar"] = new Bar { B = "bbb" };
return View();
}
what should I write in Edit.aspx view? I don't want to write names 'foo.a' and 'bar.b' manually.
String-indexed ViewData is bad. What you probably want to do is make a little wrapper class for your multi-variable view data and pass that to a strongly typed view. IE:
public class FooBarViewData
{
public Foo Foo {get; set;}
public Bar Bar {get; set;}
}
public ActionResult Edit()
{
FooBarViewData fbvd = new FooBarViewData();
fbvd.Foo = new Foo(){ A = "aaa"};
fbvd.Bar = new Bar(){ B = "bbb"};
return View(fbvd);
}
Then your view is just strongly typed to FooBarViewData and you can call members of that object using the Model property.
You have a couple of choices. First, you can reference them from ViewData and use an HtmlHelper extension. Or you could create a view-specific model and use a strongly-typed viewpage for Edit.aspx.
public class EditModel
{
public Foo foo { get; set; }
public Bar bar { get; set; }
}
public ActionResult Edit()
{
var model = new EditModel();
model.foo = new Foo { A = "aaa" };
model.bar = new Bar { B = "bbb" };
return View( model );
}
(Edit.aspx is of type ViewPage<EditModel>)
Either way, the HtmlHelper extension will pick up any initial values.
<form action="/some/process" method="post">
<%= Html.Hidden( "foo.A" ) %>
<%= Html.Hidden( "bar.B" ) %>
</form>
The solution above may be outdated. This solution seems to work for ASP.Net MVC5+.
You're going to have to use the ViewModel method. This is an excellent tutorial that you can check out.
http://tutlane.com/tutorial/aspnet-mvc/how-to-use-viewmodel-in-asp-net-mvc-with-example
You are going to have to join multiple models into one ViewModel and grab all the properties from each models you want to use into the ViewModel.
BUT, it is HIGHLY advised that you create a new Controller and a new View to accomodate the newly created ViewModel. Read the tutorial.
public class FooBarViewModel
{
public string A {get; set;} //Property for Foo
public string B {get; set;} //Property for Bar
}
public ActionResult Edit()
{
FooBarViewModel fooBarVM = new FooBarViewModel();
fooBarVM.A = "aaa";
fooBarVM.B = "bbb";
return View(fooBarVM);
}
But in this case, you should be able to pass the ViewModel into a different view. Just make sure you declare this similar directive correctly in the foobar.cshtml page.
#model FooBar.Models.FooBarViewModel
I was struggling for a while trying to get two models to work, and most of the answers I came across was meant for earlier versions of MVC.
I found the following tutorial works best for MVC 5 as suggested by David Lee:
http://tutlane.com/tutorial/aspnet-mvc/how-to-use-viewmodel-in-asp-net-mvc-with-example
I created a join and selected only the columns I needed from both Models.
I had it working with partial views
In view.cshtml
#model IEnumerable<Test.Models.TestViewModel>
...
#foreach (var item in Model)
{
<tr>
<td style="text-align:center;vertical-align:middle">
#Html.DisplayFor(modelItem => item.TestName)
...

Better way of creating repeating HTML section

I've asked this once before but without any code to look at, here I have an implementation and I'm wondering if there is a better way to accomplish this.
I want a repeating html section like so:
<div>
<input id=id1 name=id1 type=text/>
</div>
<div>
<input id=id2 name=id2 type=text/>
</div
etc
This could contain any number of input boxes which map to the List of 'something' classes I have in the model, I presently do this with a View
#using (Html.BeginForm())
{
for (int i = 0; i < Model.Somethings.Count; i++)
{
Model.Index = i;
#Html.Action("Index", "HtmlSection", Model);
}
// other stuff
}
and a partial view
#{
int index = Model.Index;
}
#Html.TextBoxFor(x => x.Somethings[index].TheProperty)
Where the model looks like this
public class HtmlSectionModel
{
public List<Something> Somethings { get; set; }
public int Index { get; set; }
}
Finally the action looks like this
public ActionResult Index(HtmlSectionModel model)
{
// do stuff
}
To me this works but isn't ideal
The partial view can now only be used within this context, it uses the top level model rather than just the 'Something' class
I have to pass an index in the model in order to get unique name's for binding, if I didn't do this then textbox would have the same name/id
This seems to me to be a common pattern so others must have solved it in other ways?
I guess what I'm after here is the MVC equivalent of Asp.Net UserControls/Webcontrols (which seem to be child actions/partial views), but, combined with model binding which seems to require unique names
What I wanted can be accomplished with editor templates
Controller
public class UsesEditorController : Controller
{
[HttpGet]
public ActionResult Index()
{
return View(new SomeModel());
}
[HttpPost]
public ActionResult Index(SomeModel model)
{
return View(model);
}
}
Model
public class Blob
{
public string Name { get; set; }
public string Address { get; set; }
public Blob()
{
Name = string.Empty;
Address = string.Empty;
}
}
public class SomeModel
{
public List<Blob> Blobs { get; set; }
public SomeModel()
{
int count = 5;
this.Blobs = new List<Blob>(count);
for (int i = 0; i < count; i++)
{
this.Blobs.Add(new Blob());
}
}
}
View
#model MyProject.Areas.EditorTemplates.Models.SomeModel
#using (Html.BeginForm())
{
for (int i = 0; i < Model.Blobs.Count; i++)
{
#Html.EditorFor(m => m.Blobs[i], "CustomEditorForBlob");
}
<input type="submit" value="Send data back" />
}
And Editor, which can be anywhere in the view folder as I'm referring to it directly
#model MyProject.Areas.EditorTemplates.Models.Blob
#Html.TextBoxFor(m => m.Name)
#Html.TextBoxFor(m => m.Address)
This renders with ids like:
<input class="k-textbox" id="Blobs_1__Name" name="Blobs[1].Name" ...
So this gives me
List item
a repeating structure, just like UserControls in Asp.Net
The editor template only refers to the Blob class, it has no knowledge of the SomeModel class
Binding works (tested it)
It looks to me like what you are trying to accomplish is unique IDs for your inputs, and you certainly don't need a partial to do this. You can output your text box inside your for loop like the following:
#Html.TextBoxFor(x => x.Somethings[i].TheProperty)
This will generate a unique id something like id="Somethings_1_TheProperty". If you don't like that id, you can certainly make your own with something like this:
#Html.TextBoxFor(x => x.Somethings[i].TheProperty, new {id="id" + (i+1)})

How to pass List from Controller to View in MVC 3

I have a List<> binded with some data in Controller action and I want to pass that List<> to View to bind with DataGrid in Razor View.
I am new to MVC.Can any one help me how to pass and how to access in View.
Passing data to view is simple as passing object to method. Take a look at
Controller.View Method
protected internal ViewResult View(
Object model
)
Something like this
//controller
List<MyObject> list = new List<MyObject>();
return View(list);
//view
#model List<MyObject>
// and property Model is type of List<MyObject>
#foreach(var item in Model)
{
<span>#item.Name</span>
}
I did this;
In controller:
public ActionResult Index()
{
var invoices = db.Invoices;
var categories = db.Categories.ToList();
ViewData["MyData"] = categories; // Send this list to the view
return View(invoices.ToList());
}
In view:
#model IEnumerable<abc.Models.Invoice>
#{
ViewBag.Title = "Invoices";
}
#{
var categories = (List<Category>) ViewData["MyData"]; // Cast the list
}
#foreach (var c in #categories) // Print the list
{
#Html.Label(c.Name);
}
<table>
...
#foreach (var item in Model)
{
...
}
</table>
Hope it helps
You can use the dynamic object ViewBag to pass data from Controllers to Views.
Add the following to your controller:
ViewBag.MyList = myList;
Then you can acces it from your view:
#ViewBag.MyList
// e.g.
#foreach (var item in ViewBag.MyList) { ... }
Create a model which contains your list and other things you need for the view.
For example:
public class MyModel
{
public List<string> _MyList { get; set; }
}
From the action method put your desired list to the Model, _MyList property, like:
public ActionResult ArticleList(MyModel model)
{
model._MyList = new List<string>{"item1","item2","item3"};
return PartialView(#"~/Views/Home/MyView.cshtml", model);
}
In your view access the model as follows
#model MyModel
foreach (var item in Model)
{
<div>#item</div>
}
I think it will help for start.

variable to show/hide link in View of ASP.NET MVC3

Fairly new to MVC3 and I am doing a simple project for testing purposes. So I have encountered a slight problem which I would like your help about.
At the moment I have this code :-
<p>
#if (UserCanCreate)
{
#Html.ActionLink("Create New", "Create")
}
My question is, how can I populate the boolean value UserCanCreate from my controller? I tried creating a property in the Controller, but the View still does not see this.
Is there maybe a better way to do this?
Thanks for your help
UPDATED with ViewModel
#model IEnumerable<DBName.ViewModels.ProfileData>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.UserName)
</td>
<td>
#Html.ActionLink("Details", "Details", new { id=item.FantaTeamID }) |
#Html.ActionLink("Delete", "Delete", new { id=item.FantaTeamID })
</td>
</tr>
}
How can I replace this code now? from where to get item.UserName?
Thanks
There are at least two different ways you can do it.
First one (better one) is using a Model to pass data. For that you should create a Model class:
//Model.cs
public class MyModel
{
public bool UserCanCreate { get; set;}
}
and specify it at the beginning of your View (use typed view):
#model %Your_model_namespace%.MyModel
...
<p>
#if (Model.UserCanCreate)
{
#Html.ActionLink("Create New", "Create")
}
And, of course, in your controller action do the following:
public ActionResult YourAction()
{
...
MyModel model = new MyModel();
model.UserCanCreate = ... ; // Determine if link shall be visible here
...
return View(model);
}
Second (faster one, although not recommended): use ViewData collection to pass values.
For example, in your controller action do:
public ActionResult YourAction()
{
...
ViewData["UserCanCreate"] = ... ; // Determine if link shall be visible here
...
return View();
}
and your view will look like:
...
<p>
#if ((bool)ViewData["UserCanCreate"])
{
#Html.ActionLink("Create New", "Create")
}
Also important: if you're trying to secure some actions that way then do not forget to add some checks for permissions inside "Create" action code - otherwise any user will be able to execute it by just entering corresponding URL in browser address bar - even if the ActionLink is not present.
UPDATE
As for your comment - you seem to be mixing domain models with view models. Domain models are the ones that represent your database entities, while view models are the ones that should be used for presentation. Generally it's controller's job to convert objects from domain to view models and vice versa.
In your case EF models are domain models.
For ViewModel you should create a separate class which will aggregate your domain model instance and any additional properties you want - like UserCanCreate.
Example model class:
public class UsersViewModel
{
public IEnumerable<DBName.Models.Users> Users { get; set; }
public bool UserCanCreate { get; set; }
... // Any other additional properties you may require
}
In your controller it will be:
public ActionResult YourAction()
{
...
UsersViewModel model = new UsersViewModel();
model.Users = ... ; // Get your IEnumerable<DBName.Models.Users> collection from DB and assign it to model property here
model.UserCanCreate = ... ; // Determine if link shall be visible here
...
return View(model);
}
and in view:
#model %Your_model_namespace%.UsersViewModel
... // use Model.Users wherever you need your IEnumerable<DBName.Models.Users>
<p>
#if (Model.UserCanCreate)
{
#Html.ActionLink("Create New", "Create")
}
When you create the model, set the value
UserCanCreate = _permissionService.Authorize(StandardPermissionProvider.AccessAdminPanel),
Your View should interact with Controller using Model. So you should create a strongly typed View and pass to it a Model instance. For example:
// ViewModel
public class MyViewModel
{
public bool UserCanCreate{get;set;}
}
// Controller
public class MyController : Controller
{
public ActionResult Index()
{
var model = new MyViewModel
{
UserCanCreate = _permissionService.Authorize(StandardPermissionProvider.AccessAdminPanel)
};
return View(model);
}
// Other Actions goes here....
}
// View
<p>
#if (Model.UserCanCreate)
{
#Html.ActionLink("Create New", "Create")
}
Your view must have a Model, for example UserModel.
Add UserCanCreate boolean property to UserModel and then you're able to access it in view.
For example:
namespace MvcApplication1.Models
{
public class UserModel
{
public bool UserCanCreate { get; set; }
}
}
and use it in the view:
#model MvcApplication1.Models.UserModel
#{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<title>Index</title>
</head>
<body>
<div>
#Model.UserCanCreate
</div>
</body>
</html>

ASP.NET MVC - Problem with EditorTemplate for ICollection<T> mapped to Enum

I have an ASP.NET MVC 3 (Razor) website, and a (simplified) model called Review:
public class Review
{
public int ReviewId { get; set; }
public bool RecommendationOne
{
// hook property - gets/set values in the ICollection
}
public bool RecommendationTwo { // etc }
public ICollection<Recommendation> Recommendations { get; set; }
}
Recommendation is as follows:
public class Recommendation
{
public byte RecommendationTypeId
}
I also have an enum called RecommendationType, which i use to map the above recommendation to. (based on RecommendationTypeId).
So to summarize - a single Review has many Recommendations, and each of those Recommendations map to a particular enum type, i expose hook properties to simplify model-binding/code.
So, onto the View:
#Html.EditorFor(model => model.Recommendations, "Recommendations")
Pretty simple.
Now, for the editor template, i want to display a checkbox for each possible RecommendationType (enum), and if the model has that recommendation (e.g on edit view), i check the box.
Here's what i have:
#model IEnumerable<xxxx.DomainModel.Core.Posts.Recommendation>
#using xxxx.DomainModel.Core.Posts;
#{
Layout = null;
}
<table>
#foreach (var rec in Enum.GetValues(typeof(RecommendationType)).Cast<RecommendationType>())
{
<tr>
<td>
#* If review contains this recommendation, check the box *#
#if (Model != null && Model.Any(x => x.RecommendationTypeId == (byte)rec))
{
#* How do i create a (checked) checkbox here? *#
}
else
{
#* How do i created a checkbox here? *#
}
#rec.ToDescription()
</td>
</tr>
}
</table>
As the comments suggest - i don't know how to use #Html.CheckBoxFor. Usually that takes an expression based on the model, but i'm how sure how to bind to the hook property based on the currently looped enum value. E.g it needs to dynamically do #Html.CheckBoxFor(x => x.RecommendationOne), #Html.CheckBoxFor(x => x.RecommendationTwo), etc.
The current solution i have (which works), involves manually constructing the <input> (including hidden fields).
But as i'm just getting the hang of editor templates, hoping someone with experience can point me in a "strongly-typed" direction.
Or is there a nicer way (HTML Helper) i can do this?
I would start by introducing a proper view model for the scenario:
public enum RecommendationType { One, Two, Three }
public class ReviewViewModel
{
public IEnumerable<RecommendationViewModel> Recommendations { get; set; }
}
public class RecommendationViewModel
{
public RecommendationType RecommendationType { get; set; }
public bool IsChecked { get; set; }
}
Then the controller:
public class HomeController : Controller
{
public ActionResult Index()
{
// TODO: query the repository to fetch your model
// and use AutoMapper to map between it and the
// corresponding view model so that you have a true/false
// for each enum value
var model = new ReviewViewModel
{
Recommendations = new[]
{
new RecommendationViewModel {
RecommendationType = RecommendationType.One,
IsChecked = false
},
new RecommendationViewModel {
RecommendationType = RecommendationType.Two,
IsChecked = true
},
new RecommendationViewModel {
RecommendationType = RecommendationType.Three,
IsChecked = true
},
}
};
return View(model);
}
[HttpPost]
public ActionResult Index(ReviewViewModel model)
{
// Here you will get for each enum value the corresponding
// checked value
// TODO: Use AutoMapper to map back to your model and persist
// using a repository
return RedirectToAction("Success");
}
}
and the corresponding view (~/Views/Home/Index.cshtml):
#model YourAppName.Models.ReviewViewModel
#{
ViewBag.Title = "Index";
}
#using (Html.BeginForm())
{
#Html.EditorFor(model => model.Recommendations)
<input type="submit" value="Go" />
}
and finally the editor template (~/Views/Home/EditorTemplates/RecommendationViewModel.cshtml)
#model YourAppName.Models.RecommendationViewModel
<div>
#Html.HiddenFor(x => x.RecommendationType)
#Model.RecommendationType
#Html.CheckBoxFor(x => x.IsChecked)
</div>
Now the view code is cleaned as it should. No ifs, no loops, no LINQ, no reflection, this is the responsibility of the controller/mapper layer. So every time you find yourself writing some advanced C# logic in your view I would recommend you rethinking your view models and adapt them as necessary. That's what view models are intended for: to be as close as possible to the view logic.

Resources