Passing only one object of a ViewModel to controller - asp.net-mvc

I have a Home ViewModel class which contains others class:
public class HomeVM
{
public ProductSearchRequest ProductSearchRequest { get; set; }
//Other class
//Other class
//Other class
}
My home/Index view has #model HomeVM, and there I have a search text input:
#using (Html.BeginForm("Search", "Product"))
{
#Html.TextBoxFor(m => m.ProductSearchRequest.SearchText)
<input type="submit" value="Search" />
}
But in my Product/Search I need to receive only ProductSearchRequest because there is other pages that uses other ViewModel but contains ProductSearchRequest.
I'm trying this:
public ActionResult Search(ProductSearchRequest request)
{
var response = new ProductSearchResponse
{
SearchText = request.SearchText,
Products = GetProductsByName(request.SearchText)
};
return View(response);
}
but it doesn't work.. request.SearchText is always null..
How can I do this?

This is just a stab in the dark, but your action, try calling the parameter 'ProductSearchRequest'
Or how about putting your whole form in a view for that action I.e. 'Search' that takes the 'ProductSearchRequest' as model?

The problem you are having is because TextBoxFor() will create an html input using a naming convention that is expected to be bound to the same type of model HomeVM as the original action.
You can try using the simple TextBox() helper method like this:
#Html.TextBox("SearchText", Model.ProductSearchRequest.SearchText)
To avoid using a string value, you can make a separate form partial that takes the ProductSearchRequest object as the model, and call:
#Html.RenderPartial("SearchForm",Model.ProductSearchRequest)
Now you can use the TextBoxFor() method like this in the partial:
#Html.TextBoxFor(m => m.SearchRequest)

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:

Object reference not set to an instance of an object in MVC5

I have an MVC5 app, and in the HomeController, I have an ActionResult defined like this:
public ActionResult BlogRSS()
{
var model = new BlogModel();
string strFeed = "http://webmysite.com/feed";
using (XmlReader reader = XmlReader.Create(strFeed))
{
SyndicationFeed rssData = SyndicationFeed.Load(reader);
model.BlogFeed = rssData;
}
return View(model);
}
Then, for this ActionResult, I created a partial view named BlogRSS, which looks like this:
#model MyApp.Models.BlogModel
#{
if (Model.BlogFeed != null)
{
<ul>
#foreach (var post in Model.BlogFeed.Items.ToList().Take(3))
{
<li><a href='#post.Links.First().Uri' target='_blank'>#post.Title.Text</a></li>
}
</ul>
}
}
And my model is defined simply like this:
public class BlogModel
{
public SyndicationFeed BlogFeed { get; set; }
}
So, the point is that I want to call that partial view in my _Layout.cshtml file, but when the website opens I get the error message specified in the title. I guess it is not calling my BlogRSS method at all. I'm calling it in the _Layout.cshtml like this:
<div class="col-md-4">
Blog
<br />
#Html.Partial("BlogRSS")
</div>
How can I solve the problem, and make sure that the corresponding ActionResult is also called before rendering the View?
The problem is that you're putting a call to a partial view which just renders the view without calling the controller and the model passed to that view is null.
There are couple ways how to fix this:
1) use Action instead of Partial
#Html.Action("BlogRSS", "Blog")
2) Define a base ViewModel which you will pass to the each view and put your feed into it.

ASP.NET MVC, passing Model from View to Controller

I'm having trouble with ASP.NET MVC and passing data from View to Controller. I have a model like this:
public class InputModel {
public List<Process> axProc { get; set; }
public string ToJson() {
return new JavaScriptSerializer().Serialize(this);
}
}
public class Process {
public string name { get; set; }
public string value { get; set; }
}
I create this InputModel in my Controller and pass it to the View:
public ActionResult Input() {
if (Session["InputModel"] == null)
Session["InputModel"] = loadInputModel();
return View(Session["InputModel"]);
}
In my Input.cshtml file I then have some code to generate the input form:
#model PROJ.Models.InputModel
#using(Html.BeginForm()) {
foreach(PROJ.Models.Process p in Model.axProc){
<input type="text" />
#* #Html.TextBoxFor(?? => p.value) *#
}
<input type="submit" value="SEND" />
}
Now when I click on the submit button, I want to work with the data that was put into the textfields.
QUESTION 1: I have seen this #Html.TextBoxFor(), but I don't really get this "stuff => otherstuff". I concluded that the "otherstuff" should be the field where I want to have my data written to, in this case it would probably be "p.value". But what is the "stuff" thing in front of the arrow?
Back in the Controller I then have a function for the POST with some debug:
[HttpPost]
public ActionResult Input(InputModel m) {
DEBUG(m.ToJson());
DEBUG("COUNT: " + m.axProc.Count);
return View(m);
}
Here the Debug only shows something like:
{"axProc":[]}
COUNT: 0
So the returned Model I get is empty.
QUESTION 2: Am I doing something fundamentally wrong with this #using(Html.BeginForm())? Is this not the correct choice here? If so, how do I get my model filled with data back to the controller?
(I cannot use "#model List< Process >" here (because the example above is abbreviated, in the actual code there would be more stuff).)
I hope someone can fill me in with some of the details I'm overlooking.
Change your view to some thing like this to properly bind the list on form submission.
#using(Html.BeginForm()) {
for(int i=0;i<Model.axProc.Count;i++){
<span>
#Html.TextBoxFor(model => model.axProc[i].value)
</span>
}
<input type="submit" value="SEND" />
}
In #Html.TextBoxFor(stuff => otherstuff) stuff is your View's model, otherstuff is your model's public member.
Since in the View you want to render input elements for the model member of a collection type (List), you should first create a separate partial view for rendering a single item of that collection (Process). It would look something like this (name it Process.cshtml, for example, and place into the /Views/Shared folder):
#model List<PROJ.Models.Process>
#Html.TextBoxFor(model => p.value)
Then, your main View would look like this:
#model PROJ.Models.InputModel
#using(Html.BeginForm()) {
foreach(PROJ.Models.Process p in Model.axProc){
#Html.Partial("Process", p)
}
<input type="submit" value="SEND" />
}
Also, check that the loadInputModel() method actually returns something, e.g. not an empty list.

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?

How to pass data to the view in mvc asp.net?

let me ask the question first.
Where is the correct place to call a function that load a list of values to be display on a view?
I create a controller like this
public ActionResult Create()
{
SeaModel newSea = new SeaModel();
return View("Season/CreateSea", newSea);
}
//I not quite sure if this should go here or in another place
partial class seaDataContext
{
public List<string> getSeaSettings()
{
var seaSettings = from p in settings
where p.setting == "periods"
select p.value;
return seaSettings.ToList<string>();
}
}
The model is like
public class SeaModel
{
[Required(ErrorMessage="*")]
[Display(Name = "Period Name")]
public string periods { get; set; }
}
Which create a view like
#using (Html.BeginForm()) {
#Html.ValidationSummary(true, "Please correct the following errors.")
<fieldset>
<legend>Fields</legend>
<div class="editor-label">
#Html.LabelFor(model => model.periods)
</div>
<div class="editor-field">
#Html.Select(model => model.periods, ****My doubt comes here****)
#Html.ValidationMessageFor(model => model.periods)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
so, How and where do I pass the return of getSeaSettings() to the view?
Thanks
best practice is to make a Selectlist in your Model for this dropdown.
however you also can use the more easy option: using ViewData
public ActionResult Create()
{
SeaModel newSea = new SeaModel();
ViewData["myDropDown"] = new SelectList(listOfObjects, "valueOfTheObjectLikeID", "NameYouWantToShowInDropdown");
return View("Season/CreateSea", newSea);
}
then:
#Html.Select(model => model.periods, ViewData["myDropDown"] as SelectList)
dont forget in your [HttpPost] method to also fill in the viewdata if you'r validation fails, so the dropdown can be rebuilt.
You need to look at repository pattern. Have a look at this tutorial at asp.net site
http://www.asp.net/mvc/tutorials/creating-model-classes-with-linq-to-sql-cs
Stefanvds's approach was what I used to do.
But I found out there is a better way using additionalViewData.
Use this EditorFor HTML Helper extension method.
http://msdn.microsoft.com/en-us/library/ff406462.aspx
Instead of passing Select List Items into ViewData in the Controller, you do this in your View.
Pass in your list items as an anonymous object for the additionalViewData parameter.
Important thing is to use the same name as your Property Name.
#Html.EditorFor(
m => m.MyPropertyName,
new { MyPropertyName = Model.ListItemsForMyPropertyName }
);
Of course, you are passing in a View Model object.
public class MyViewModel
{
public int MyPropertyName;
public IList<SelectListItem> ListItemsForMyPropertyName;
}
EditorFor method uses your existing Editor View Templates.
So you don't need to specify CSS class names and HTML attributes again like when you use the Html.DropDown( ) method.
For example,
//------------------------------
// additionalViewData
//------------------------------
#Html.EditorFor(
m => m.MyPropertyName,
new { MyPropertyName = Model.ListItemsForMyPropertyName }
)
//------------------------------
// traditional approach requires to pass your own HTML attributes
//------------------------------
#Html.DropDown(
"MyPropertyName",
Model.ListItemsForMyPropertyName,
new Dictionary<string, object> {
{ "class", "myDropDownCssClass" }
}
);
//------------------------------
// DropDownListFor still requires you to pass in your own HTML attributes
//------------------------------
#Html.DropDownListFor(
m => m.MyPropertyName,
Model.ListItemsForMyPropertyName,
new Dictionary<string, object> {
{ "class", "myDropDownCssClass" }
}
);
That is why I like the additionalViewData approach more.
Because, the HTML code rendered relies on the Editor Templates completely.
Also, using specialized View Models make your code cleaner and easier to maintain.
Hope it helps.

Resources