ASP.NET MVC Form post - asp.net-mvc

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)%><%}%>

Related

Can I append data from controller to url and still render same page?

I have a simple MVC application that presents an IQ test to the person.
I have a controller call TestMyIQController and on this controller I have three action methods and corresponding to three views.
1) Index.cshtml
2) Questions.cshtml
3) Score.cshtml
On the first page localhost:12345/TestMyIQ/Index of the application I ask the user to input their First and Last name and click button Next to go to second page.
On the second page of the application localhost:12345/TestMyIQ/Questions I present a few multiple choice questions and I have the user select True/False for each question. Once the user complete all the question she can click button Submit to see her IQ score.
On the final score page localhost:12345/TestMyIQ/Score I show the score via a ViewBag object.
However, I want to know how to modified the final page url so that I can append the First Name of the user? For example if the person name is John Doe then I want my final score url will be like this localhost:12345/TestMyIQ/Score?firstname=John.
Note it is not necessary to append the First Name to all other url...because I only want it on the final score page.
Please help me. Thanks
Considering you received the first name in the Questions action, and added as a property of your model, you can add it to your form as a hidden field:
Questions.cshtml
using(Html.BeginForm())
{
#Html.HiddenFor(x => x.UserFirstName);
// rest of form
}
TestMyIQController
[HttpPost]
public ActionResult Question(QuestionModel model)
{
// form processing
return RedirectToAction("Score", new{ firstName = model.UserFirstName })
}
Because in the Question page you stored it as a hidden field, it will only show up in the url of the score page.
But keep in mind that while this is ok if you're learning and just want to do some cool stuff to show your friends, this isn't the best way to handle all of this.
EDIT to add more info on the problems of using this method.
The negative is that anyone can change the URL. Nothing stops me from changing ?firstName=foo to ?firstName=bar. Second, names can contain invalid characters for URLs, which will need to be encoded. Third, it's overall bad design.
There are better ways to handle this, but it depends on the what you need from it. Will the users be able to share the url? If not, you can add it to the Session. This is definitely easier to implement in your current design. If they'll do, you could store the result in a SQL table, and share the url as ?scoreId=f88f9426-04d7-4ae2-8e15-a4bbd8d6faad.
Not sure I understand your needs, but you dont have to store the name in url, you can use for example session variable, but if you insist on it being on url, why not just redirect(urlWithParameter).
For a good listing of possibilities, see
Msprogrammer
Session may be choice to store the name and retrieve it on the score page.
[HttpPost]
public ActionResult Index(UserModel model)
{
// save the name
Session["FirstName"] = model.FirstName;
return RedirectToAction("Questions")
}
On the score method, retrieve the name from session
[HttpPost]
public ActionResult Score(ScoreModel model)
{
// save the name
string firstName = (string)Session["FirstName"];
return RedirectToAction("Index")
}
Hope it helps.

Clear All Fields on MVC4 Razor View on both: Loading and after Back to Page

What is the most effective method in order to make all the fields on an MVC4 Razor empty when loading it (for example first loading or after backing to the page again)? If it is possible could you please suggest me a way with the help of Razor properties instead of using Javascript/jQuery.
It's a little difficult to make out what you're trying to do, but let's see if I can help.
Firstly, if you simply wanted to clear out a form's values after it's been posted, you can do that like so:
[HttpPost]
public ActionResult Index(ViewModel model)
{
ModelState.Clear();
model = new ViewModel();
return View(model);
}
Simply creating a new ViewModel isn't enough, as the ModelState dictionary will try to repopulate the form with the old values. This does what you want, but isn't really leveraging MVC to do what you want.
The better way to do it would be to redirect back to the action you use to display your form. Something like this:
public ActionResult Create()
{
var model = new ViewModel();
return View(model);
}
This is simply passing in an empty model to your form. Once the user fills out the form, and it's posted back to the server, you can handle it like so:
[HttpPost]
public ActionResult Create(ViewModel model)
{
if (ModelState.IsValid)
{
// Form data is valid so redirect back to display the form again
return RedirectToAction("Create");
}
// If we get here, redisplay the form with the fields filled in
// and errors shown
return View(model);
}
Simply calling a ModelState.Clear() will do the trick. You shouldn't have to instantiate the view model again.
A view displays (or collect) information about a Model.
So, if you pass an Empty model (that is: properties in null or blank or default values) it will "clear all fields".
All you have to do is invoking again the Action that displays the view, now passing an empty model.
EDIT:
You can do it in javascript, but then you have to duplicate and maintain the logic of what are default values.

ASP.net MVC create related object using partials

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

Completely not understanding View Data for MVC right now, need some serious help & code examples

I really need to maintain this string called "filterParams" in my MVC application. After the user enters some search parameters he clicks submit, and the grid is rebinded with that parameter. That works great. I also save the filterParams data in a Javascript variable, so when the user pages, and the OnDataBinding event is raised, the filter is also passed through that ajax call as well. this is all well and good however there is a huge issue, because when the user updates a question, all the results dissapear because it returns to the View and it does not have any data there. The way I'm using ViewData isn't working, and I could use your help, because if I can store it in ViewData and access it, it would fix my problems. I cannot use TempData because there are a number of other Actions that can be called in between Select and Update...Long question short, how do I implement ViewData correctly to store and retrieve a string in my controller?
Here are some code snippets.
[GridAction]
public ActionResult GetAllQuestion(string filterParams)
{
var _filterParams = new List<string>();
_filterParams.Add(filterParams);
ViewData["filterParams"] = _filterParams;
return View(new GridModel(QuestionManager.Instance.GetQuestion(filterParams)));
}
[GridAction]
public ActionResult EditQuestion(int id, QuestionDTO pQuestion)
{
// var _question = QuestionManager.Instance.GetQuestion(id,false).FirstOrDefault();
// TryUpdateModel(_question);
var _filterParams = (List<string>)ViewData["filterParams"];
var filterParams = _filterParams[0];
QuestionManager.Instance.UpdateQuestion(pQuestion);
// return View(new GridModel(QuestionManager.Instance.GetQuestion(id, false)));
return View(new GridModel(QuestionManager.Instance.GetQuestion(filterParams)));
}
in my aspx page
Html.Telerik().Grid<QuestionDTO>()
.DataBinding(dataBinding => dataBinding.Ajax().Select("GetAllQuestion", "Question", new { filterParams = string.Empty }).Update("EditQuestion", "Question").Insert("CreateQuestion", "Question"))
How can I get this to work please? Help is appreciated
ViewBag/ViewData only works for sending data from an action to a view. It does not get populated by the Model Binder when a request is made to an action, and its state is not saved between requests because ASP.net MVC is entirely stateless. In other words, the ViewData dictionary is always empty at the start of a request.
Meaning this line in your EditQuestion action will not work:
var _filterParams = (List<string>)ViewData["filterParams"];
ViewData is empty, so _filterParams will be null.
You have to manually send filterParams to the EditQuestion action just as you do for the GetAllQuestions action.
Perhaps a better alternative would simply be to persist filterParams using a temp cookie on the client side.
So, just to defy all the misinformation I've read on the subject, TempData infact does persist through multiple action calls in the controller and was able to be used to implement the functionality I needed.
Why just not store the data in Session?
Here's a good explanation with examples
http://rachelappel.com/when-to-use-viewbag-viewdata-or-tempdata-in-asp.net-mvc-3-applications

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.

Resources