When rendering a view the model is not updated only the viewbag - asp.net-mvc-3

So.... I have an action in my controller that basically copies the current model and returns a new view based on that copy. To inform the user that this is a copy I append a message via the viewbag stating that it's a copy. All seemed to be working until I noticed that it's not the copy that is being used when rendering the new view instead it's the original, but the viewbag on the other hand is updated so the message is shown.
Hmm, don't know if that's understandable so I'll try to show what i mean with some pseudo-code as well:
Model
public class Model{
[ScaffoldColumn(false)]
[HiddenInput(DisplayValue = false)]
public Guid Id { get; set; }
public string Name { get; set; }
}
View
<input type="submit" name="Copy" value="#_("Copy")"/>
Controller
public ActionResult Copy(model) {
ViewBag.Message = _("This is a copy.");
var clone = model.Clone();
return View("Index", clone);
}
I'm having a real hard time trying to wrap my head around this so any help/tips/pointers are really appreciated.
Oh, I've stepped through the code several times to ensure that the clone is really a clone. The only thing that differentiates them is the Id property and that is the new one in the controller but when the view renders it's back to the old one.

You need to clear the ModelState collection before returning the clone because the HtmlHelpers prefer to reuse the posted data:
public ActionResult Copy(model) {
ViewBag.Message = _("This is a copy.");
var clone = model.Clone();
ModelState.Clear();
return View("Index", clone);
}
You can read more about this feature in this artice: ASP.NET MVC Postbacks and HtmlHelper Controls ignoring Model Changes.

Related

Required ModelValidation just for new objects ASP.NET MVC

I have this issue since yesterday.
In my User model I have a [NotMapped] called "ConfirmPassword". I don´t save it on the database but I use it on my Create form as a always to validate the data input for new users.
Since than, it´s ok. The problem is on my [HttpPost] Edit action. I should be able to edit some user's data without type and confirm the password. I use both Password and ConfirmPassword as a way to confirm the old password and informe the new one, if I wanna change the password. But, if I don´t, I leave them blank.
I have used already the code below to be able to pass the ModelState.IsValid() condition and it worked:
ModelState["Password"].Errors.Clear();
ModelState["ConfirmPassword"].Errors.Clear();
But, just before the db.SaveChanges(), as User user view model is considered, it has both properties empty and I got:
Property: ConfirmPassword Error: The field ConfirmPassword is invalid.
The question is: How could I skip de Required model validation when I want to update an object?
I read already about custom ModelValidations with classes extending ValidationAttribute and
DataAnnotationsModelValidator but I am not doing it right.
Any idea? How could I create a custom model validation that checks if the UserId property is null or not. It´s a nice way to check if I'm in Create or Edit action.
Thanks,
Paulo
Using the domain objects as your ViewModel will leads you to a condition of less scalability. I would opt for seperate ViewModels specific for the Views. When i have to save the data i map the ViewModel to the Domain model and save that. In your speciific case, i would create 2 ViewModels
public class CustomerViewModel
{
public string FirstName { set;get;}
public string LastName { set;get;}
}
And i will Have another ViewModel which inherits from the above class, for the Create View
public class CustomerCreateViewModel :CustomerViewModel
{
[Required]
public string Password { set;get;}
[Required]
public string ConfirmPassword { set;get;}
}
Now in my Get actions, i use this ViewModel
public ActionResult Create()
{
var vm=new CustomerCreateViewModel();
return View(vm);
}
and of course my View(create.cshtml) is now binded to this ViewModel
#model CustomerCreateViewModel
<h2>Create Csustomer</h2/>
//Other form stuff
Similarly for My Edit Action,
public ActionResult Edit(int id)
{
var vm=new CustomerViewModel();
var domainCustomer=repo.GetCustomerFromID(id);
if(domainCustomer!=null)
{
//This manual mapping can be replaced by AutoMapper.
vm.FirstName=domainCustomer.FirstName;
vm.LastName=domainCustomer.LastName;
}
return View(vm);
}
This view is bounded to CustomerViewModel
#model CustomerViewModel
<h2>Edit Info of #Model.FirstName</h2>
//Other form stuff
In your POST Actions, Map it back to the Domain object and Save
[HttpPost]
public ActionResult Create(CustomerCreateViewModel model)
{
if(ModelState.IsValid)
{
var domainCust=new Customer();
domainCust.FirstName=model.FirstName;
repo.InsertCustomer(domainCust);
//Redirect if success (to follow PRG pattern)
}
return View(model);
}
Instead of writing the Mapping yourself, you may consider using AutoMapper library to do it for you.

Model not updated from view to controller

I'm trying to do that:
Create a Model, add it on a session and send it to the view.
Change Model fields on my view
Get the Model from session updated on my controller
The problem is that my model is never updated when I'm changing values on textboxes, I'm sure that I'm missing something with razor,
View:
#model MvcTestApp.Models.Car
<div class="b1">
<div class="b2">#Html.EditorFor(e => e.KM)</div>
<div class="b2">#Html.EditorFor(e => e.RegistrationNumber)</div>
</div>
#Html.ActionLink("Car", "sendCar")
Controller:
On SendCar, I would like to get the model updated.
namespace MvcTestApp.Controllers
{
public class CarController : Controller
{
public ActionResult Show()
{
var model = new MvcTestApp.Models.Car()
{
RegistrationNumber ="12345",
KM = "12345"
};
Session["temp"] = model;
return View("Show",Session["temp"]);
}
public ActionResult sendCar()
{
return View("Show", Session["temp"]);
}
}
}
Model:
namespace MvcTestApp.Models
{
public class Car
{
[DataType(DataType.Text)]
public string KM { get; set;}
[DataType(DataType.Text)]
public string RegistrationNumber { get; set;}
}
}
You need to make your sendCar controller to update the model. Currently, all the changes you do will only persist locally until you navigate away from the page. You need to post the changed model back to the server.
Take a look at the "Handling edits" part of this example to see how it can be done:
Asp.net tutorials
The way to do this is by wrapping your model details in a form with a submit function. Then in your sendCar method take in a Car object and the model binding will take care of setting everything on the new object.
If you're wanting to persist this (I assume this is just for testing purposes?) then perhaps make your car that you're returning in your show method a class variable.
You should read a beginner tutorial about ASP.NET MVC, which will explain you how to send data from a form to a controller, as it seems you are absolutely not aware of how to do this.
You are not missing 'something', you are missing all about sending data from forms to controllers.

MVC3 Razor - Models and Views

I have an action that creates a List and returns it to my view..
public ActionResult GetCustomers()
{
return PartialView("~/Views/Shared/DisplayTemplates/Customers.cshtml", UserQueries.GetCustomers(SiteInfo.Current.Id));
}
And in the "~/Views/Shared/DisplayTemplates/Customers.cshtml" view I have the following:
#model IEnumerable<FishEye.Models.CustomerModel>
#Html.DisplayForModel("Customer")
Then I have in the "~/Views/Shared/DisplayTemplates/Customer.cshtml" view:
#model FishEye.Models.CustomerModel
#Model.Profile.FirstName
I am getting the error:
The model item passed into the dictionary is of type System.Collections.Generic.List`1[Models.CustomerModel]', but this dictionary requires a model item of type 'Models.CustomerModel'.
Shouldn't it display the Customer.cshtml for every item in the collection in the Customers.cshtml?
Help!
I am not sure why you are calling a partial view like this. If it is a Customer Specific view, why not put it under Views/Customer folder ? Remember ASP.NET MVC is more of Conventions. so i would always stick with the conventions (unless abosultely necessary to configure myself) to keep it simple.
To handle this situation, i would do it in this way,
a Customer and CustomerList model/Videmodel
public class CustomerList
{
public List<Customer> Customers { get; set; }
//Other Properties as you wish also
}
public class Customer
{
public string Name { get; set; }
}
And in the action method, i would return an object of CustomerList class
CustomerList customerList = new CustomerList();
customerList.Customers = new List<Customer>();
customerList.Customers.Add(new Customer { Name = "Malibu" });
// you may replace the above manual adding with a db call.
return View("CustomerList", customerList);
Now there should be a view called CustomerList.cshtml under Views/YourControllerName/ folder. That view should look like this
#model CustomerList
<p>List of Customers</p>
#Html.DisplayFor(x=>x.Customers)
Have a view called Customer.cshtml under Views/Shared/DisplayTemplates with this content
#model Customer
<h2>#Model.Name</h2>
This will give you the desired output.
Your view is expecting a single model:
#model FishEye.Models.CustomerModel // <--- Just one of me
You're passing it an anonymous List:
... , UserQueries.GetCustomers(SiteInfo.Current.Id) // <--- Many of me
You should change your view to accept the List or determine which item in the list is supposed to be used before passing it into the View. Keep in mind, a list with 1 item is still a list and the View is not allowed to guess.

How to Post Partial View Data?

Any input much appreciated :)
I want to know one thing whether I can post multiple partial views data in MVC?(means i want to update partial views data to DATABASE)
Here is the Example:
Model:-
public class PassengerViewModel
{
public List<PassengerModel> Passengers { get; set; }
public ContactModel Contact { get; set; }
}
Controller:-
[RequiredAuthentication]
public ActionResult Passenger()
{
var passengrViewMdl = new PassengerViewModel()
{
Contact = new ContactModel(),
Passengers = psngrService.LoadPassengers(Convert.ToInt32(Session["LPORefNO"]))
};
return View(passengrViewMdl);
}
[HttpPost]
public ActionResult Passenger(PassengerViewModel passengerViewModel)
{
Here i want to update Passengers & Contact information
}
View:-
#model QR.LPO.Core.Models.PassengerViewModel
#{
ViewBag.Title = "Add Passengers";
}
#using (Html.BeginForm())
{
#Html.Partial("_Passenger", Model.Passengers);
#Html.Partial("_PassengerContact", Model.Contact);
<input type="submit" value="Submit" />
}
Thanks.
Yes, indeed you can, but, controller usually works only with one model per request, so either your model should have declared within it properties of both partial submodels, or submodels themselves.
This is possible due to HTML specifications, all data on form, which has submit buttom is send to submit action url.
This will almost work as you have it - there's nothing inherent to partials that would prevent this, in the end the html that's output is all that's important.
The problem with your code is that presumably the model of your _Passenger view is of type Passengers and the model of your _PassangerContact view is of type Contact. What this means is that if you standard HtmlHelper extensions (like Html.Textbox(...) or Html.TextboxFor(...) the fields they generate will not have full names like Contact.Name, but instead just names relative to their model, like Name. This will cause modelbinding to fail in your post action.
You can solve this in a number of ways.
Simply use the same model type (PassengerViewModel) in your sub-views, and write code like #Html.TextboxFor(m => m.Contact.Name).
Instead of using Html.Partial, use Html.EditorFor(...). This passes the proper prefix information into the child view so the field names are generated properly.
Explicitly set the prefix yourself
Like this:
#{
var childViewData = new ViewDataDictionary(this.ViewData);
childView.TemplateInfo.HtmlFieldPrefix = "Contact";
}
#Html.Partial("_PassengerContact", Model.Contact, childViewData)
You could also look at creating a Html.PartialFor overload yourself, as described in this stackoverflow question: ASP.NET MVC partial views: input name prefixes

html.TextBoxFor and html.Textbox, POSTing values, model in parameters

Alright guys, Need some help!
Im working with asp.net mvc3 razor (and am fairly new to it but did lots of web forms)
Okay so onto the problem
My question revolves around submitting a view.
I have a very complicated model that my view is based off (strongly typed).
I want to return the model into the arguments in the HttpPost method of the controller. do basically:
public ActionResult Personal()
{
DataModel dataModel = new DataModel();
FormModel model = new FormModel();
model.candidateModel = dataModel.candidateModel;
model.lookupModel = new LookupModel();
return View(model);
}
[HttpPost]
public ActionResult Personal(FormModel formModel)
{
if (ModelState.IsValid)
{
//stuff
}
return View(formModel);
}
Now...
I'm having trouble getting values into the formModel parameter on the post method.
This works (meaning i can see the value)but is tedious as i have to write exactly where it sits in a string every single field:
#Html.TextBox("formModel.candidateModel.tblApplicant.FirstName", Model.candidateModel.tblApplicant.FirstName)
It renders like this:
<input name="formModel.candidateModel.tblApplicant.FirstName" id="formModel_candidateModel_tblApplicant_FirstName" type="text" value="Graeme"/>
This doesn't work:
#Html.TextBoxFor(c => c.candidateModel.tblApplicant.FirstName)
It renders like this:
<input name="candidateModel.tblApplicant.FirstName" id="candidateModel_tblApplicant_FirstName" type="text" value="Graeme"/>
Now I'm assuming the problem lies in the discrepancy of the id's
So please answer me this:
Am i going about this the right way
Why doesn't textboxfor get the right value/id, and how do i make it get the right value/id so i can retrieve it in a POST(if that is even the problem)?
Additionally, it seems that textboxfor is restrictive, in the manner that if you have a date time, how do you use the .toshortdate() method? This makes me think textboxfor isn't useful for me.
Quick clarification:
when i say textboxfor isn't working, it IS getting values when i GET the form. So they fill, but on the POST / submission, i can't see them in the formModel in the parameters.
Another side note:
None of the html helpers work, this is the problem. They aren't appearing in modelstate either.
Thanks everyone for the help
Answer:
html.TextBoxFor and html.Textbox, POSTing values, model in parameters
It was a problem in my view somewhere, i replaced all the code with the snippet in this answer and it worked.
Thank you again
Am i going about this the right way
Yes.
Why doesn't textboxfor get the right value/id, and how do i make it get the right value/id so i can retrieve it in a POST(if that is even the problem)?
There is something else in your code that makes this not work. It's difficult to say since you haven't shown all your code. Here's a full working example which illustrates and proves that there's something else going on with your code:
Model:
public class FormModel
{
public CandidateModel candidateModel { get; set; }
}
public class CandidateModel
{
public Applicant tblApplicant { get; set; }
}
public class Applicant
{
public string FirstName { get; set; }
}
Controller:
public class HomeController : Controller
{
public ActionResult Index()
{
return View(new FormModel
{
candidateModel = new CandidateModel
{
tblApplicant = new Applicant
{
FirstName = "fn"
}
}
});
}
[HttpPost]
public ActionResult Index(FormModel formModel)
{
// the username will be correctly bound here
return View(formModel);
}
}
View:
#model FormModel
#using (Html.BeginForm())
{
#Html.EditorFor(c => c.candidateModel.tblApplicant.FirstName)
<button type="submit">OK</button>
}
Additionally, it seems that textboxfor is restrictive, in the manner
that if you have a date time, how do you use the .toshortdate()
method? This makes me think textboxfor isn't useful for me.
I agree that TextBoxFor is restrictive. That's why I would recommend you always using EditorFor instead of TextBoxFor. It will allow you to simply decorate your view model property with the [DisplayFormat] attribute and voilà. You get any format you like.
For example:
public class MyViewModel
{
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime CreatedAt { get; set; }
}
and in the view:
#model MyViewModel
#Html.EditorFor(x => x.CreatedAt)
will format the date exactly as you expect.
the model binder uses the name to bind the values to the model, and the html helpers e.g. Html.TextBoxFor uses the body of the lambda expression to set the name, however you can specify the name yourself which you are doing by using the Html.TextBox( helper
#Html.TextBoxFor(x=>x.candidateModel.tblApplicant.FirstName),
new{#Name="formModel.candidateModel.tblApplicant.FirstName"})
If your view is strongly typed, try the helper bellow, instead call each helper on each property
#Html.EditorForModel()
#Html.EditorFor(m => m.candidateModel)
#Html.EditorFor(m => m.lookupModel)
Update:
Well, have tried to use viewmodel to simplify this task? And when you get back the data you can map your real models. keep your views clean will give you less headaches in the future. Additionally you could use AutoMapper to help you.
Here a example if you think that will help you.
http://weblogs.asp.net/shijuvarghese/archive/2010/02/01/view-model-pattern-and-automapper-in-asp-net-mvc-applications.aspx

Resources