ASP.net MVC create related object using partials - asp.net-mvc

I am relatively new to ASP and MVC but have got on ok so far. I am using the DB first approach and have my DB tables all setup (it is an existing DB cleaned up with FKs etc).
I have a route of FKs:
Contact
- LettingContact
- Landlord
- Tenant
I would like to be able to use partials to display the data e.g. /Contact/_Create will hold the Contact info i.e. Title, Forename, Surname and will be used both by /Contact/Create and /Tenant/Create. I managed to get it working not using the partials and just using the field on the Tenant/Create html form and showing the relevant data from the models. To the Tenant/Create in the controller i did the following (doing the following stopped me getting null exceptions in the partial)
Tenant tenant = new Tenant();
LettingsContact lettingsContact = new LettingsContact();
Contact contact = new Contact();
tenant.LettingsContact = letContact;
tenant.LettingsContact.Contact = contact;
return View(tenant)
Now the View is
//using Html.BeginForm etc
#{
Html.RenderPartial("../Contact/_Create", Model.LettingsContact.Contact);
Html.RenderPartial("_Create", Model);
}
<input type="submit" value="create">
//rest of html
Now when I click the submit button it goes to my /Tenant/Create post event.
[HttpPost]
public ActionResult Create(Tenant tenant)
{
if (ModelState.IsValue)
{
tenant.ContactID = Guid.NewGuid();
tenant.LettingsContact.Contact.ContactID = tenant.ContactID;
db.Tenants.AddObject(tenant);
db.SaveChanges();
return RedirectToAction("Index");
}
}
However the line which reads tenant.LettingContact.Contact.ContactID crashes will a NullReferenceException to the LettingsContact is null. Any reason why the partials are not maintaining the Models?
Also if there is a better way of doing this please let me know as this is still very new to me.
The reason I want to use partials is that it will enable me to reuse the forms in jQuery modal forms etc.

If you want a form to post back information that you don't want displayed on the page you should use a hidden field. Have a look at Html.Hidden() and Html.HiddenFor().
Hidden on msdn: http://msdn.microsoft.com/en-us/library/system.web.mvc.html.inputextensions.hidden.aspx
HiddenFor on msdn: http://msdn.microsoft.com/en-us/library/system.web.mvc.html.inputextensions.hiddenfor.aspx

Related

Does MVC remember checkbox values after submit?

I'm a php head and getting to grips with ASP.NET MVC 5.
In php, after submitting checkbox fields, in order to post back the form with the checkboxes you checked initially set to true, you have to run a if isset on em.
However reading up on model binding in mvc, it seems that this is done automatically for you, i.e checkboxes are returned after form submit checked, if originally selected, using either the HTML.CheckBox or HTML.CheckBoxFor helpers.
Is this the case, or am I expecting too much of MVC?
No, ASP.NET MVC doesn't remember checkbox values after they're submitted. Being an HTTP application as soon as ASP.NET MVC has rendered the HTML it ends the request and forgets everything about what it's just done. Then upon submitting a form ASP.NET MVC processes the incoming HTTP request and maps it to your model via its model binding (more on how it does this in a moment).
Having come from a PHP background myself this is one of those questions I always had when starting with ASP.NET MVC.
With ASP.NET MVC you have to remember that you're working within the context of a complete framework, and in order to ensure you're as productive as possible ASP.NET MVC will take care of a lot of the mundane work for you - ASP.NET MVC's model binding is a perfect example of this.
When submitting a form, the ASP.NET MVC framework will parse all incoming post data and attempt to automatically map it to the values you're providing it via your controller action.
So where as in PHP you'd normally do something along the lines of:
if(isset($_POST['checkboxValue'])) {
$checkboxVal = $_POST['checkboxValue'];
}
ASP.NET MVC will automaltically bind the incoming post data to your action parameter like so:
[HttpPost]
public ActionResult Submit(bool checkboxValue){
}
It does this by checking the parameter name (checkboxValue) matches that of the post data array key, and that the type also matches up. For instance, if you were to change the above checkboxValue from a boolean to a string and change the name, then ASP.NET MVC's model binding will be unable to match the property to the post data and will not automatically set the value for you.
It's also worth noting that ASP.NET MVC's model binding doesn't know how you created the checkbox.
The HTML.CheckBox and HTML.CheckBoxFor html helpers are purely a means to make it easier for you to create the HTML. If you were to manually write the HTML yourself then the model binder will still successfully bind the submitted data.
Edit:
As #DrinkBird has quite rightly pointed out, you're also able to access all of your form's post data by using the FormCollection instance like so:
[HttpPost]
public ActionResult Submit(FormCollection postData){
}
This collection represents all of the data posted to the Submit action.
Yes, model-binding should allow you to retrieve the value of a checkbox on submission.
if your model looks like:
public class myModel
{
public bool myBool {get; set;}
}
and in your HTML, you've used the helper
#Html.CheckBoxFor(m => m.myBool)
Then in your post action to handle the submission:
[HttpPost]
public ActionResult MyAction(myModel model)
{
var whatsThis = model.myBool;
}
...whatsThis will be true if the checkbox was checked, false if not.
Part of why this works is that when you use #html.CheckBoxFor, it also places a hidden form field that will pass false if the box is unchecked, to aid with model binding -- if it didn't, per HTTP there would be no varibalbe 'myBool' submitted in the post-vars collection.
If you return this model back into the form (say, if it didn't validate), then the form will re-present the checkbox in whatever state it was in on submission:
[HttpPost]
public ActionResult MyAction(myModel model)
{
if(!ModelState.IsValid)
{
return View(model);
}
else
{
//do success
}
}

MVC - Returning a model from controller for Edit Profile

I'm new to MVC and not finding an example on the proper way of returning a typed model to the view. I need to create a view to allow an authenticated user to edit some of their profile.
As a test I created a controller action that returns an ApplicationUser model and view page that displays the profile in form fields. This works, but it contains all of the user profile and that's not what I want. To test I wrote the following:
Public ActionResult EditProfile()
{
ApplicationUser user = UserManager.FindById(User.Identity.GetUserId());
return View(user)
}
Of course, this does work and I realize I can display whatever I like in the view, though I'm not sure if it's a best practice to be returning all of the user's profile to the view when I only need to allow editing a few of their settings.
So I created a new ViewModel with only the fields needed and a new view based on that model. It works, but I still don't feel I'm doing it properly. In the controller action I did this:
public ActionResult EditProfile()
{
ApplicationUser user = UserManager.FindById(User.Identity.GetUserId());
EditProfileViewModel model = new EditProfileViewModel();
model.Email = user.Email;
model.Company = user.Company;
model.Name = user.Name;
model.PhoneNumber = user.PhoneNumber;
model.CountryCode = user.CountryCode;
model.StateProvince = user.StateProvince;
model.Language = user.Language;
model.StateProvinceCode = user.StateProvinceCode;
return View(model);
}
This just seems unnecessary and horrible to maintain. I'm sorry for asking such a basic question. I have honestly searched the 'net for examples and I know they are out there, but I'm not finding what I feel applies to my question.
Btw, the controller is decorated with [Authorize] and so I assume this action/view is only be accessed by an authenticated user.
I will understand better if someone could show me an example of the proper way of populating a model and passing it to the controller. Seems this should be able to be done in just a couple of lines of code.
Well, first of all there is nothing to worried about as everything is handled on server side. if you wish to create custom model its seems overload as you have to now do 2 more transaction
Copy data from original model to customized model.
In [HttpPost] action you have to get original model data and overwrite newly updated field from customized model.
Better if you use original model, just take necessary fields in view and in [HttpPost] action you need to fetch original record and replace fields get from the view.

How do I pass the userId into the model ASP.NET MVC?

I've had a thorough search around but really can't find anything addressing the scenario I'm facing (oddly because I'd have thought it's quite a common thing to do).
Background
I'm creating an application with ASP.NET MVC 4 and Entity Framework 5 Code First. For the purpose of this question, think of it as a blogging application with posts and users.
Project
The post model requires that every post have a corresponding UserId.
With the ASP.NET MVC 4 Membership it is easy to find the username of the person logged in with
User.Identity.Name.
This isn't ideal, we want the ID, but a query such as this can search the db for the name and get the ID.
db.UserProfiles.Single(a => a.UserName == User.Identity.Name);
Problem
The problem arises when trying to create a post. Model.IsValid is false, as no UserId is being passed in from the view. Obviously, as the user isn't expected to enter their ID.
I've tried putting the ID value into the ViewBag and using a #Html.Hidden() field in the view, however I've had no success with this. Model.IsValid always returns false.
Should this information be input through the create view? Or should it be done directly in the controller? Its quite a frustrating problem as I have the information and just need to figure how to pass it into the model.
CONTROLLER CODE
This is basically just the default scaffolded code. The commented code is how I tried setting the model value directly from the controller, however that was little more than trial and error.
//
// POST: /Post/Create
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Post post)
{
if (ModelState.IsValid)
{
//var userId = db.UserProfiles.Single(a => a.UserName == User.Identity.Name);
//post.User.UserId = userId.UserId;
db.Posts.Add(post);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(post);
}
Be careful with hidden fields. Anyone could put whatever value they want in that field (i.e. they could spoof another user). You'd be better off caching the ID in the session at login, and using that value.
This is a typical case where you want to create an EditModel as a data transfer object (DTO) between your view and controller layers.
Create a class BlogPostEditModel that has all properties you need the user to fill in when creating a new blog post. Then, map this type (e.g. using AutoMapper) to your BlogPost entity, and fill in the user ID as well.
To use built-in validation such as Model.IsValid(), put the data annotations attributes on the DTO instead.
Honestly, I would have the value assigned via the controller. If you had someone messing with your html via Firebug, they could actually change the id before it was passed and submitted to your form. I would remove it from your Create view and submit from the controller.

How do i submit the complete viewmodel to an other view?

I'm using MVC3 (razor) and i'm trying to get the following working.
I have a list of snippets. These snippets have some general settings and then have a translation for an unknown ammount of languages.
Now i'm trying to do the following:
On the 'Create' page (url: Screen) of a snippet i set the general settings. under that there is a list of filled translations (empty at the start). When you press the 'Opslaan' button, i want the form to save the general settings and the list of translations.
When i push the 'Add' button i want to submit the complete viewmodel (settings + list of translations) to an other page where i can fill in a translation. After i filled in a translations, i want to return to this page (url: Screen). Here, a translation is filled in the list.
Now i'm doing someting wrong, because i cant get the viewmodel to submit to the 2nd page.
this is my code:
button 'add translation':
#Html.ActionLink("Add", "CreateTranslation", new { oSnippeteditviewmodel = this.Model }, null)
SnippetController:
public ActionResult Create()
{
SnippetEditViewModel oItem = new SnippetEditViewModel();
oItem.lSnippetsPerLanguage = new List<SnippetPerLanguageEditViewModel>();
return View(oItem);
}
[HttpPost]
public ActionResult Create(SnippetEditViewModel Snippeteditviewmodel)
{
if (ModelState.IsValid)
{
Snippeteditviewmodel.Bookmark = Snippeteditviewmodel.Bookmark.Replace(' ', '_');
_repoSnippet.CreateSnippet(Snippeteditviewmodel);
return RedirectToAction("Index");
}
return View(Snippeteditviewmodel);
}
public ActionResult CreateTranslation(SnippetEditViewModel oSnippeteditviewmodel)
{
return View(oSnippeteditviewmodel);
}
And in the controller, action CreateTranslation the object 'oSnippeteditviewmodel' stays null.
annyone who has a simular problem? Or a solution?
First, you should try to generate action link like this
#Html.ActionLink("Add", "CreateTranslation", this.Model, null)
In this case mvc will try to pass correctly serialized model values for your link and if your model is simple enough, CreateTranslations will get its model correctly. But, I would not do it that way. Generated link is static. What if user changes Snippet values on client side? When it comes to adding Translation, all the changed form values will be lost (Link will pass initial, server generated values). So, you should try one of the followings
Create the form with two buttons, one for CratingTranslation and one for Saving. When creating translation, dynamically change form's action and method parameters to GET the CreateTranslation action. This way, form will serialize all its current Snippet settings and pass to desired action, and you get the current snippet model passed to CreateTranslation action.
Use ajax. Just dynamically inject translation creation input fields into same page. That's simple and more user friendly (no bundle of navigations), and more http traffic is reserved (Passing all the translations and snippet to second page, and then returning all of these + 1 translation could get you in trouble). I would reccomend this approach. This is far more simple than first or your approaches.
I am not getting you properly but if you wanna add data by "create" controller then you don't need to specify object in "oSnippeteditviewmodel". You can get all form data by
Request.Form["controlName"]
and fill the Snippeteditviewmodel data member by above and save that.

ASP.NET MVC Form post

I'm a beginner in ASP.NET MVC application. What I'm trying to do is creating a form with some inputs that the user will be filling up, and once the user click the post button, I want the form to be posted with the information filled up and ready for printing. The way I'm doing it right now is as follow:
// the controller that returns the initial form using ReportCreate.aspx which creates a Html form
public ActionResult ReportCreate()
{
return View(viewData);
}
// my post action which gets the information for the submitted form
// and use the ReportPost.aspx to view a similar page as ReportCreate.aspx but with all the Html.TexBox inputs replaced with their values obtained from the submitted form
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult ReportCreate(FormCollection form)
{
ReportFormData formData = new ReportFormData();
formData.Date = form["date"];
formData.Company = form["company"];
formData.SiteNameA = form["siteNameA"];
formData.SiteNameB = form["siteNameB"];
formData.FreqBand = form["freqBand"];
formData.FileNumber = form["fileNumber"];
formData.ResponseDate = form["responseDate"];
formData.SiteAddressA = form["siteAddressA"];
formData.SiteAddressB = form["siteAddressB"];
this.TempData.Add("viewData", viewData);
return View("ReportPost", formData);
}
What I don't like about this way, is that I have to aspx pages (ReportCreate.aspx & ReportPost.aspx) that I need to keep similar and modify both of them together if I need to do any changes to the look of the form. I feel there should be a more professional way to handle this common issue. I tried to look it up online, but couldn't get anything. Please let me know. Thanks a lot in advance.
If you want to display the posted data in the same form just use the same aspx page as when you created the data.
However the usual way is to have one page for:
Create - to input values first time and after a succesful input redirect to
Details - where the data is not on a form but as regular text
If you need to modify the data use
Edit
To display a collection of data use
Index
Another point to note is that you dont need to use manually set all of the values from the form to your ReportFormData class, instead do:
[HttpPost]
public ActionResult Create(ReportFormData formData)
{
if(!ModelState.Isvalid){
return View(formData);
}
else
{
RedirectToAction("Index");
}
}
If all the formatting is the same other than the textbox should be a label, just use a conditional in your view to determine if you should display a textbox or not.
<%if(model.ReadOnly){%><%=Html.LabelFor(m => m.Company)%><%else%><%=Html.TextBoxFor(m => m.Company)%><%}%>

Resources