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

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)
...

Related

Form returns null viewmodel to the Action [duplicate]

I have a model object structure with a Foo class that contains a Bar with a string value.
public class Foo
{
public Bar Bar;
}
public class Bar
{
public string Value { get; set; }
}
And a view model that uses that structure like this
public class HomeModel
{
public Foo Foo;
}
I then have a form in view that in Razor looks something like this.
<body>
<div>
#using (Html.BeginForm("Save", "Home", FormMethod.Post))
{
<fieldset>
#Html.TextBoxFor(m => m.Foo.Bar.Value)
<input type="submit" value="Send"/>
</fieldset>
}
</div>
</body>
In html that becomes.
<form action="/Home/Save" method="post">
<fieldset>
<input id="Foo_Bar_Value" name="Foo.Bar.Value" type="text" value="Test">
<input type="submit" value="Send">
</fieldset>
</form>
Finally the controller to handle the post loos like this
[HttpPost]
public ActionResult Save(Foo foo)
{
// Magic happends here
return RedirectToAction("Index");
}
My problem is that Bar in Foo is null once it hits the Save controller action (Foo is created but with an null Bar field).
I thought the model binder in MVC would be able to create the Foo and the Bar object and set the Value property as long as it looks like the above. What am I missing?
I also know my view model is a bit over complicated and could be simpler but I for what I'm trying to do I'd really help me if I could use the deeper object structure. The examples above uses ASP.NET 5.
Firstly, the DefaultModelBinder will not bind to fields so you need to use properties
public class HomeModel
{
public Foo Foo { get; set; }
}
Secondly, the helpers are generating controls based on HomeModel but you posting back to Foo. Either change the POST method to
[HttpPost]
public ActionResult Save(HomeModel model)
or use the BindAttribute to specify the Prefix (which essentially strips the value of prefix from the posted values - so Foo.Bar.Value becomes Bar.Value for the purposes of binding)
[HttpPost]
public ActionResult Save([Bind(Prefix="Foo")]Foo model)
Note also that you should not name the method parameter with the same name as one of your properties otherwise binding will fail and your model will be null.
I just discovered another reason this can happen, which is if your property is named Settings! Consider the following View model:
public class SomeVM
{
public SomeSettings DSettings { get; set; } // named this way it will work
public SomeSettings Settings { get; set; } // property named 'Settings' won't bind!
public bool ResetToDefault { get; set; }
}
In code, if you bind to the Settings property, it fails to bind (not just on post but even on generating the form). If you rename Settings to DSettings (etc) it suddenly works again.
I had the same problem and after I followed #Stephen Muecke steps I realized that the problem was caused because my inputs were disabled (I was disabling them with JQuery on document ready) as you can see it here: How do I submit disabled input in ASP.NET MVC?. At the end I used read-only instead of disabled attribute and all the values were sent successfully to the controller.
I had the same problem, but once I created a HIDDEN FIELD for the foreign-key...it all worked just fine...
FORM EXAMPLE:
#using (Html.BeginForm("save", "meter", FormMethod.Post))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
#Html.HiddenFor(model => Model.Entity.Id)
#Html.HiddenFor(model => Model.Entity.DifferentialMeter.MeterId)
#Html.HiddenFor(model => Model.Entity.LinearMeter.MeterId)
#Html.HiddenFor(model => Model.Entity.GatheringMeter.MeterId)
... all your awesome controls go here ...
}
ACTION EXAMPLE:
// POST: /Meter/Save
[HttpPost]
public ActionResult Save(Meter entity)
{
... world-saving & amazing logic goes here ...
}
PRETTY PICTURES:

ViewViewdata.Model; what model does?

ViewData.Model
i know that Viewdata contains the data that has been returned by view via
return view(//data);
but what does this .Model represents ?
Controller:
using Testing_linq.Models;
namespace Testing_linq.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Message = "Welcome to ASP.NET MVC!";
var DataContext = new RegistrationDataContext();
var Registration = DataContext.SelectRegistration_sp();
return View(Registration);
}
View:
<table>
#foreach(SelectRegistration_spResult reg in (IEnumerable<Object>)ViewData.Model)
{
<tr>
<td>#reg.userEmail </td>
</tr>
}
</table>
I'm using LInq to Sql classes in model.
The Model represents whatever dynamic information is required to render the page, and is something you provide to your view. For example, if you were going to create a View that shows a person's First name and Last name, you might come up with the following model:
public sealed class PersonViewModel
{
public string FirstName {get; set; }
public string LastName {get; set; }
}
It's preferable to use strongly-typed views, so the first line in your .cshtml file would be:
#model MyNameSpace.PersonViewModel
And when your controller returns the view, you would pass what needs to be rendered in to it:
public ActionResult Index()
{
return View(new PersonViewModel() { FirstName = "Moo", LastName = "Juice" });
}
ViewData.Model refers to the instance of the PersonViewModel passed in, and is accessed in your view via Model shortcut:
<body>
<p>
Hello #Model.FirstName !!
</p>
</body>

MVC 3 using #model twice

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
}

EF4.3 MVC 4 Ninject CustomModelBinderAttribute for Abstract base class losing post values

I will try to explain as best i can... Huge post coming as i'm not 100% where the problem lies. It probably has a really simple fix but i'm pulling hair at the min.
I have an abstract base class with a navigation property to a document type like so
public abstract class Document: Identity
{
public int DocumentTypeId { get; set; } // this remains and alls good
public virtual DocumentType DocumentType { get; set; } // this dissappears.
}
This DocumentType object contains a string value for the namespace for the type of object that it is. As an example I then have a class derived the document abstract class like so
public class Blog : Document
{
//properties excluded to keep this post from being HUGE!
}
They all have properties which are saving back to the database. however the document type is missing. Not the DocumentTypeId, that is there, but the actual DocumentType, I need to navigate into it to get the namespace value but it is missing.
It now gets a bit more complicated. I am using a ninject generic repository that injects, in this case the Blog into the view. Here is an example controller.
public class ContentController : Controller
{
private IRepository<Document> repo;
public ContentController(IRepository<Document> _repo)
{
repo = _repo;
}
public ActionResult Settings(string name) //name is unique!
{
Document d = repo.First(x => x.Name == name);
return View(d); // all properties are present
}
[HttpPost]
public ActionResult Settings([AbstractBind()] Document obj)
{
repo.Save(obj); // documenttype is missing documenttypeId is present.
return View(obj);
// this does not work but refresh does?!
//Document d = repo.First(x => x.Name == obj.name);
//return View(d);
}
}
because the view is bound to an abstract class I am using the editor for model like so
#model Core.Entities.Documents.Abstract.Document
#{
ViewBag.Title = "Index";
Layout = "~/Areas/Admin/Views/Shared/_CMSContent.cshtml";
}
<h2>Settings - #Model.Name</h2>
#using (Html.BeginForm())
{
#Html.Hidden("Namespace", Model.DocumentType.Namespace);
#Html.ValidationSummary(true)
#Html.EditorForModel();
<div class="editor-label"> </div>
<div class="editor-field">
<input type="submit" value="Save" class="btn" />
</div>
<br class="clear" />
#Html.ActionLink("< Back to List", "Index")
}
Notice that i am adding a hidden field for the namespace contained within the view. Also notice that i am using a custom binding method to figure out what type is actually being edited, by going through the following method:
protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
{
var type = Assembly.GetAssembly(typeof(Document)).GetType(bindingContext.ValueProvider.GetValue("Namespace").AttemptedValue);
var model = Activator.CreateInstance(type);
bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, type);
return model;
}
this figures out what type it is, (Blog). How do i get the DocumentType reference back into my model on the HTTP post?
I have tried adding a hidden field for the type like this #Html.HiddenFor(x => x.DocumentType); but it doesn't work.
I also tried going and getting it from the repository again after saving (commented out in the example) but no joy. If i refresh the page then the field is back again as Ninject and EF figures out that the Id is a foreign key that points to the DocumentType.
I could go and get the document type by the id that is in the "Document obj" and attach it back to the object before returing it to the view but i think that this would be terrible practise.
Any ideas?

MVC3 using CheckBox with a complex viewmodel

Right guys. I need your brains as I can't find a way to do this properly.
I have a view model:
public class EditUserViewModel
{
public User User;
public IQueryable<ServiceLicense> ServiceLicenses;
}
User is unimportant as I know how to deal with it.
ServiceLicenses has the following implementation:
public class ServiceLicense
{
public Guid ServiceId { get; set; }
public string ServiceName { get; set; }
public bool GotLic { get; set; }
}
Getting a checked list of users is cool. It works like a charm.
<fieldset>
<legend>Licenses</legend>
#foreach (var service in Model.ServiceLicenses)
{
<p>
#Html.CheckBoxFor(x => service.GotLic)
#service.ServiceName
</p>
}
</fieldset>
The problem I'm having is getting the updated ServiceLicenses object with new checked services back to the HttpPost in my controller. For simplicity lets say it looks like this:
[HttpPost]
public ActionResult EditUser(Guid id, FormCollection collection)
{
var userModel = new EditUserViewModel(id);
if (TryUpdateModel(userModel))
{
//This is fine and I know what to do with this
var editUser = userModel.User;
//This does not update
var serviceLicenses = userModel.ServiceLicenses;
return RedirectToAction("Details", new { id = editUser.ClientId });
}
else
{
return View(userModel);
}
}
I know I am using CheckBox the wrong way. What do I need to change to get serviceLicenses to update with the boxes checked in the form?
i understand that ServiceLicenses property is a collection and you want MVC binder to bind it to you action parameters property. for that you should have indices attached with inputs in your view e.g
<input type="checkbox" name = "ServiceLicenses[0].GotLic" value="true"/>
<input type="checkbox" name = "ServiceLicenses[1].GotLic" value="true"/>
<input type="checkbox" name = "ServiceLicenses[2].GotLic" value="true"/>
Prefix may not be mandatory but it is very handy when binding collection property of action method parameter. for that purpose i would suggest using for loop instead of foreach and using Html.CheckBox helper instead of Html.CheckBoxFor
<fieldset>
<legend>Licenses</legend>
#for (int i=0;i<Model.ServiceLicenses.Count;i++)
{
<p>
#Html.CheckBox("ServiceLicenses["+i+"].GotLic",ServiceLicenses[i].GotLic)
#Html.CheckBox("ServiceLicenses["+i+"].ServiceName",ServiceLicenses[i].ServiceName)//you would want to bind name of service in case model is invalid you can pass on same model to view
#service.ServiceName
</p>
}
</fieldset>
Not using strongly typed helper is just a personal preference here. if you do not want to index your inputs like this you can also have a look at this great post by steve senderson
Edit: i have blogged about creating master detail form on asp.net mvc3 which is relevant in case of list binding as well.

Resources