View Model looks like this:
public class AsmenysInfoViewModel2
{
public asmenys_info Asmenys_info { get; set; }
public List<miestai> Miestai { get; set; }
public string Test { get; set; }
}
And there are two actions. Get and Post.
public ActionResult Index(long? id)
{
var model = new AsmenysInfoViewModel2();
model.Test = "Test";
model.Asmenys_info = BllFactory.DalFactory.AsmenysInfoDal.GetById(id.Value);
return View(model);
}
[HttpPost]
public ActionResult Index(AsmenysInfoViewModel2 asmenys_info)
{
var model = asmenys_info;
return View(model);
}
And my view looks like this:
#model MODELS.AsmenysInfoViewModel2
#{
ViewBag.Title = "Index";
}
#using (Html.BeginForm("Index", "AsmenysInfo", FormMethod.Post))
{
#Html.ValidationSummary()
#Html.TextBoxFor(m => m.Asmenys_info.adresas)
<input type="submit" value="Išsaugoti" />
}
Doesn't matter if I use EditorFor or TextBoxFor - result is same. My model property "Asmenys_info" on posting is always null. If my class AsmenysInfoViewModel2 would not contain asmenys_info type property and would contain only "string, int etc" (no strongly typed) - it would work.
My question is :
How to post View Model which has strongly typed property which on posting would not be null?
Your model has a property named Asmenys_info and the parameter in your POST method is also named asmenys_info. Internally the DefaultModelBinder reads the values of the form data which includes a value for Asmenys_info and attempts to set property Asmenys_info to that value but it fails because there is no conversion from a string to a complex object.
Change the name of the parameter to anything other than a name of a property in your model and it will bind fine, for example
[HttpPost]
public ActionResult Index(AsmenysInfoViewModel2 model)
Change the below line with another object name
public ActionResult Index(AsmenysInfoViewModel2 asmenys_info)
in above method use any other name of object instead of asmenys_info.
because while mvc framework map your model with object there is confution in asmenys_info and Asmenys_info property of AsmenysInfoViewModel2 class.
Related
I'm getting the apparently classic exception:
InvalidOperationException: The model item passed into the ViewDataDictionary is of type 'PieShop.ViewModels.HomeViewModel', but this ViewDataDictionary instance requires a model item of type 'System.Collections.Generic.IEnumerable`1[PieShop.Models.Pie]'
when trying to render a page in ASP.NET Core MVC.
I've seen a lot of answers for this question, but all seem to be the result of an obvious datatype mismatch between what's passed into the view from the controller and what's declared in the view itself. In my case, they match perfectly. Here's my controller:
public class HomeController : Controller
{
private readonly IPieRepository pieRepository;
public HomeController(IPieRepository pieRepository)
{
this.pieRepository = pieRepository;
}
public IActionResult Index()
{
var pies = pieRepository.GetAllPies();
var vm = new HomeViewModel()
{
SomeData = "haiod",
Title = "Welcome to the Pie Shop"
};
return View(vm);
}
}
Here's the view model:
public class HomeViewModel
{
public string Title { get; set; }
public string SomeData { get; set; }
}
And here's the view (which is called Views/Home/Index.cshtml:
#model PieShop.ViewModels.HomeViewModel
<h1>#Model.SomeData</h1>
The model passed into the view from the controller and what the view is declaring as its model are clearly the exact same type. What gives?
Turns out I had declared a type for the model in the shared _Layout.cshtml because I copy/pasted it during some refactoring:
#model IEnumerable<PieShop.Models.Pie>
So... just a copypasta error :facepalm:
My POST controller isn't able to capture the ViewModel parameter I set and I'm very confused as I have a different set of POST controller and it can capture the ViewModel parameter.
My code looks like this,
View Page
#model MyProject.Web.ViewModels.MyViewModel
#{
ViewBag.Title = "Home";
ViewBag.Description = "My Project";
ViewBag.SubDescription = "My Project Tool";
Layout = null;
}
#using (Html.BeginForm())
{
#Html.TextBoxFor(m => m.Filter)
<input type="submit" class="btn btn-primary btn-inline-right input-tab" value="Search" />
}
Controller
using MyProject.Web.ViewModels;
[HttpGet]
[Route("Home/Index")]
public async Task<ActionResult> Index()
{
...await API integration code here...
return View(MyViewModel);
}
[HttpPost]
[Route("Home/Index/{viewmodel}")]
public ActionResult Index(MyViewModel viewmodel) <-- all properties of viewmodel are NULL
{
return View();
}
View Model
using MyProject.Web.Models;
using System.Collections.Generic;
namespace MyProject.Web.ViewModels
{
public class MyViewModel
{
public User UserInfo;
public List<Client> Clients;
public string Filter;
}
}
I feel this is a very small mistake, maybe due to overlooking too much. Hopefully someone could take a look and help.
The problem is with the route you have defined on the top of your Post action [Route("Home/Index/{viewmodel}")]
You don't need that {viewmodel} in that URL as you are not posting anything in the query string, you are posting a complex object inside the body of your HTTP Post.
Remove that route and it should work.
also, ASP.NET mvc maps the inputs to the Model properties based upon the name attributes on them like <input name="abc"> will map this input to a property named abc on a ViewModel or just a parameter. In your case #Html.TextBoxFor(m => m.Filter) does that automatically.
Hope this helps.
use this I hope useful:
#using (Html.BeginForm("Index", "HomeController", FormMethod.Post))
Change from public string Filter to property public string Filter {get;set;}
and change route to [Route("Home/Index")] instead of [Route("Home/Index/{viewmodel}")].
I tested and it worked.
public class MyViewModel
{
public User UserInfo { get; set; }
public List<Client> Clients { get; set; }
public string Filter { get; set; }
}
[HttpPost]
[Route("Home/Index")]
public ActionResult Index(MyViewModel viewmodel)
{
return View();
}
I have a ViewModel that has a complex object as one of its members. The complex object has 4 properties (all strings). I'm trying to create a re-usable partial view where I can pass in the complex object and have it generate the html with html helpers for its properties. That's all working great. However, when I submit the form, the model binder isn't mapping the values back to the ViewModel's member so I don't get anything back on the server side. How can I read the values a user types into the html helpers for the complex object.
ViewModel
public class MyViewModel
{
public string SomeProperty { get; set; }
public MyComplexModel ComplexModel { get; set; }
}
MyComplexModel
public class MyComplexModel
{
public int id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
....
}
Controller
public class MyController : Controller
{
public ActionResult Index()
{
MyViewModel model = new MyViewModel();
model.ComplexModel = new MyComplexModel();
model.ComplexModel.id = 15;
return View(model);
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
// model here never has my nested model populated in the partial view
return View(model);
}
}
View
#using(Html.BeginForm("Index", "MyController", FormMethod.Post))
{
....
#Html.Partial("MyPartialView", Model.ComplexModel)
}
Partial View
#model my.path.to.namespace.MyComplexModel
#Html.TextBoxFor(m => m.Name)
...
how can I bind this data on form submission so that the parent model contains the data entered on the web form from the partial view?
thanks
EDIT: I've figured out that I need to prepend "ComplexModel." to all of my control's names in the partial view (textboxes) so that it maps to the nested object, but I can't pass the ViewModel type to the partial view to get that extra layer because it needs to be generic to accept several ViewModel types. I could just rewrite the name attribute with javascript, but that seems overly ghetto to me. How else can I do this?
EDIT 2: I can statically set the name attribute with new { Name="ComplexModel.Name" } so I think I'm in business unless someone has a better method?
You can pass the prefix to the partial using
#Html.Partial("MyPartialView", Model.ComplexModel,
new ViewDataDictionary { TemplateInfo = new TemplateInfo { HtmlFieldPrefix = "ComplexModel" }})
which will perpend the prefix to you controls name attribute so that <input name="Name" ../> will become <input name="ComplexModel.Name" ../> and correctly bind to typeof MyViewModel on post back
Edit
To make it a little easier, you can encapsulate this in a html helper
public static MvcHtmlString PartialFor<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression, string partialViewName)
{
string name = ExpressionHelper.GetExpressionText(expression);
object model = ModelMetadata.FromLambdaExpression(expression, helper.ViewData).Model;
var viewData = new ViewDataDictionary(helper.ViewData)
{
TemplateInfo = new System.Web.Mvc.TemplateInfo
{
HtmlFieldPrefix = string.IsNullOrEmpty(helper.ViewData.TemplateInfo.HtmlFieldPrefix) ?
name : $"{helper.ViewData.TemplateInfo.HtmlFieldPrefix}.{name}"
}
};
return helper.Partial(partialViewName, model, viewData);
}
and use it as
#Html.PartialFor(m => m.ComplexModel, "MyPartialView")
If you use tag helpers, the partial tag helper accepts a for attribute, which does what you expect.
<partial name="MyPartialView" for="ComplexModel" />
Using the for attribute, rather than the typical model attribute, will cause all of the form fields within the partial to be named with the ComplexModel. prefix.
You can try passing the ViewModel to the partial.
#model my.path.to.namespace.MyViewModel
#Html.TextBoxFor(m => m.ComplexModel.Name)
Edit
You can create a base model and push the complex model in there and pass the based model to the partial.
public class MyViewModel :BaseModel
{
public string SomeProperty { get; set; }
}
public class MyViewModel2 :BaseModel
{
public string SomeProperty2 { get; set; }
}
public class BaseModel
{
public MyComplexModel ComplexModel { get; set; }
}
public class MyComplexModel
{
public int id { get; set; }
public string Name { get; set; }
...
}
Then your partial will be like below :
#model my.path.to.namespace.BaseModel
#Html.TextBoxFor(m => m.ComplexModel.Name)
If this is not an acceptable solution, you may have to think in terms of overriding the model binder. You can read about that here.
I came across the same situation and with the help of such informative posts changed my partial code to have prefix on generated in input elements generated by partial view
I have used Html.partial helper giving partialview name and object of ModelType and an instance of ViewDataDictionary object with Html Field Prefix to constructor of Html.partial.
This results in GET request of "xyz url" of "Main view" and rendering partial view inside it with input elements generated with prefix e.g. earlier Name="Title" now becomes Name="MySubType.Title" in respective HTML element and same for rest of the form input elements.
The problem occurred when POST request is made to "xyz url", expecting the Form which is filled in gets saved in to my database. But the MVC Modelbinder didn't bind my POSTed model data with form values filled in and also ModelState is also lost. The model in viewdata was also coming to null.
Finally I tried to update model data in Posted form using TryUppdateModel method which takes model instance and html prefix which was passed earlier to partial view,and can see now model is bound with values and model state is also present.
Please let me know if this approach is fine or bit diversified!
This is how I have made a previous post as you can see here.
must retrieve the list from the database
I have tried to make my foreach which have been previously described. but it causes problems for not running my foreach in through while making the mistake on it.
Index.cshtml
#foreach (var u in Model)
{
<div class="col-md-6 col-sm-6">
<div class="plan">
<h3>#u.Name<span>$#u.Price</span></h3>
<p>#u.Text</p>
</div>
</div>
}
and undervisningController.cs
// GET: Undervisning
public ActionResult Index()
{
DatabaseClasseDataContext db = new DatabaseClasseDataContext();
var model = db.Packages.ToList();
return View(model);
}
And the top on index.cshtml have i:
#model MentorOrdblind_MVC.Models.Undervisning.Undervisning
Model Undervisning.cs
public class Undervisning
{
public string Name { get; set; }
public decimal Price { get; set; }
public int Hours { get; set; }
public string Text { get; set; }
}
You are passing your view a List<T> but your model is not a type of IEnumerable. So your view is only expecting a single object of the type Undervisning and not a collection.
Use this:
#model IEnumerable<MentorOrdblind_MVC.Models.Undervisning.Undervisning>
Change your model delcaration to:
#model IEnumerable<MentorOrdblind_MVC.Models.Undervisning.Undervisning>
At this moment your model is a single class, not a list of objects
Always keep in mind what is being passed from controller action to view. If you pass only model from the action then use the model reference in the respective view of the action. If you pass List then use IEnumerable model reference in the view.
If you pass list from action then in the view use:
#model IEnumerable<your model> in the top as reference
If you pass model without a list then use:
#model your model
In your case you are passing list so use IEnumerable of your desired model class.
Thanks
Good Day,
I wonder how to save the information in a create.
#model Request.Models.Chamados
#model Request.Models.InteracoesChamados
#{
ViewBag.Title = "Create";
}
as shown in the two tables above only that of course does not work.
please give me an example of this because it confused me.
NOTE: So for clarity, I fill out a form and save to 2 tables when I hit save.
environment:
Windows 7,
Visual Studio 2010,
C #,
MVC3 + Razor Entity Framework
There seems to be a few things here but for starters, you can only declare one model per view.
You could create a ViewModel that has both of those above, e.g.
public class ChamodosViewModel{
public Chamados Chamados {get;set;}
public InteracoesChamados InteracoesChamados {get;set;}
}
and then in your view
#model ChamodosViewModel
Do not use the Domain model for your view. Create a new POCO class which is specific for your view. Let's call it ViewModel, in general.
public class ChamodoVM
{
[Required]
public string ChamdoName { set;get;}
[Required]
public string InteracoName { set;get;}
//other properties here as needed
}
Now in yout GET action create an object of this class and pass to the View method.
public ActionResult Create()
{
var vm=new ChamodoVM();
return View(vm);
}
Make your view strongly typed to the ViewModel class.
#model ChamodoVM
#using(Html.BeginForm())
{
#Html.LabelFor(x=>x.ChamodoName)
#Html.TextBoxFor(x=>x.ChamodoName)
#Html.LabelFor(x=>x.InteracoName)
#Html.TextBoxFor(x=>x.InteracoName)
<input type="submit" />
}
When user submit the form, read the values from view model and assign it to an object of your domain modal and save. Thanks to MVC model binding. :)
[HttpPost]
public ActionResult Create(ChamodoVM model)
{
if(ModelState.IsValid)
{
var domainModel=new Chamodo();
domainModel.Name=model.ChamodoName;
domainModel.Interaco=new Interaco();
domainModel.Interaco.Name=model.InteracoName;
yourRepositary.SaveClient(domainModel);
//If saved successfully, Redirect to another view (PRG pattern)
return RedirectToAction("ChamodoSaved");
}
return View(model);
}