Adding a save button to the index page - c#

I have written an ASP.NET application that users access to check fields off. Instead of the user having to go into the edit page and hitting save, I want them to be able to just check it off on the index page and it either saves automatically or they can click the save button on the index page. I have made the fields EditorFor instead of Display for, and added the save button to the page. However, I am not sure how to implement the code to save in the controller.
Here is the code I have been trying out on my viewcontroller, but it says "does not contain a definition for save"
public virtual ActionResult Save(Doctor model)
{
Doctor.Save();
}

You are calling Save() on class Doctor which means there must be a static method in Doctor class. If it is not static but it does existing in the Doctor class then call the save method by model.Save().
If you are using EF then make sure inside the Save() method you're attaching the updated values on model object to respective EF entity or adding the object to the context in case new item is being created.

View
#using (Html.BeginForm("Save", "Doctor", FormMethod.Post))
{
<p>
#Html.LabelFor(model => model.Foo):
#Html.EditorFor(model => model.Foo)
#Html.ValidationMessageFor(model => model.Foo)
</p>
<p>
#Html.LabelFor(model => model.barImage)
<input type="file" name="Image" />
</p>
<p>
<input type="submit" value="Save" />
</p>
}
Controller
[HttpPost]
public ActionResult Create(Doctor doc)
{
your_dbcontext db = new your_dbcontext();
db.Add<Doctor>(doc);
db.SaveChanges();
return RedirectToAction("Index");
}

Related

Calling ActionResult from PartialView never hits

Pretty new with MVC and going through a few tutorials. Have done the following:
Add a controller called CustomerController.
Add 2 methods
public ActionResult Render()
{
// Go to a third party WebAPI and get the results in a List
return PartialView("CustomerList", custList);
}
public ActionResult SomeTest()
{
Response.Redirect("Somepage");
}
I then add a page (LandingView.cshtml) and create a PartialView called CustomerList and add the below code to the LandingView page
#Html.Action("Render", "Customer")
When i view this page it renders the page with a list of customers. The HTML for the PartialView is
#using (Html.BeginForm("SomeTest", "Customer"))
{
<div class="container">
#foreach (var i in Model)
{
<a href="#i.Url">
<div class="product-grid__item__name">#i.Title</div><br />
<div class="product-grid__item__price">#i.Price.ToString("C")</div>
</a>
<input type="button" id="btnGo" value="Go" />
}
</div>
}
When i click the button it never hits the SomeTest method? In debug mode i have put a breakpoint on Render and SomeTest, Render hits on page load but when clicking Go it never hits the SomeTest method?
What am i missing here?
Set the 'type' attribute value of the input element to "submit" not "button". This will trigger the form submission on click.
<input type="submit" id="btnGo" value="Go" />
You may experience some build errors because the SomeTest() controller method is expecting a return value of type ActionResult.

ASP net core MVC Call diferent actions under 1 form

I have my this in my controller:
[HttpPost]
public IActionResult Save(int IdentifikaceZ, ReklamaceModel model)
{
_db.Add(model);
_db.SaveChanges();
return RedirectToAction("MyMainView");
}
#using (Html.BeginForm("Save", "MyMainView", FormMethod.Post))
{
....
<input id="Insert" name="Insert" value="Insert" type="submit">
<input id="Edit" name="Edit" value="Edit" type="submit">
}
What I need is to Save/Edit info inside Form but I dont know how to tell server to decide which to do. So when I click button A or button B it does same thing. I need it to do seperate things but with same elemetns (elements inside form) Thanks for any help.
The buttons are named, and the way named buttons work is that only the one that is clicked makes it into the POST. As such, you can simply check for the presence of one key or the other in the form data:
if (Request.Form.ContainsKey("Insert"))
// do insert
if (Request.Form.ContainsKey("Edit"))
// do edit
First, add a property string ActionType to your ReklamaceMode model.
Then change your HTML to:
#using (Html.BeginForm("Save", "MyMainView", FormMethod.Post))
{
....
<input id="Insert" name="ActionType" value="Insert" type="submit">
<input id="Edit" name="ActionType" value="Edit" type="submit">
}
Now in your csharp code:
[HttpPost]
public IActionResult Save(int IdentifikaceZ, ReklamaceModel model)
{
// check model.ActionType, it will be either Insert or Edit
}
Add and update operations can be handled with one form and one method as well.
Add id as hidden form control, for existing records this field will have a value which is the relevant record id. For new records the value will be 0 by default (assuming id type is int).
#using (Html.BeginForm("Save", "MyMainView", FormMethod.Post))
{
....
#Html.HiddenFor(x => x.Id)
<input type="submit" name="submit" value="Save" />
}
on the backend check for the id value, if it is > 0 then you trigger update, otherwise it is a new record.
[HttpPost]
public IActionResult Save(int IdentifikaceZ, ReklamaceModel model)
{
if(model.Id > 0)
_db.Update(model);
else
_db.Add(model);
_db.SaveChanges();
return RedirectToAction("MyMainView");
}
That was a simplified implementation, in a real life project you need to do more control over the model before adding/updating. For example it is recommended to use an input model then bind the values to the db model...
if ou still need to use multiple submit buttons in one form to target different backend actions see Multiple Submit Buttons for standard form and ajax forms as well.
Here you can do like this:::
<form method="post" asp-controller="Home">
<a id="Insert" name="Insert" value="Insert" type="submit" asp-action="Delete" asp-route-DeleteID="#model.DeleteID"/>
<a id="Edit" name="Edit" value="Edit" type="submit" asp-action="Edit" asp-route-DeleteID="#model.EditID"/>
</form>
[HttpPost] public IActionResult Delete(int id) {
}
[HttpPost] public IActionResult Edit(int id) {
}

Persisting Collections in ViewModel

I've been struggling for quite some time now with trying to maintain a list of objects when the ViewModel is submitted back to the controller. The ViewModel receives the list of objects just fine, but when the form is submitted back to the controller the list is empty. All of the non-collection properties are available in the controller, so I'm not sure what the issue is. I have already read the guide a few people have referenced from Scott Hanselman here
From what I can see, everyone solves this by building an ActionResult and letting the model binder map the collection to a parameter:
Controller:
[HttpPost]
public ActionResult Submit(List<ConfigurationVariable> variables)
{
}
View:
#for (int i = 0; i < Model.ConfigurationVariables.Count; i++)
{
<div class="row">
<div class="col-xs-4">
<label name="#Model.ConfigurationVariables[i].Name" value="#Model.ConfigurationVariables[i].Name" />
</div>
</div>
<div class="row">
<div class="col-xs-4">
<input type="text" class="form-control" name="#Model.ConfigurationVariables[i].Value" value="#Model.ConfigurationVariables[i].Value" />
</div>
</div>
}
What I really want is to be able to pass my ViewModel back to the controller, including the ConfigurationVariables List:
Controller:
[HttpPost]
public ActionResult Submit(ReportViewModel report) //report.ConfigurationVariables is empty
{
}
View:
#for (int i = 0; i < Model.ConfigurationVariables.Count; i++)
{
<div class="row">
<div class="col-xs-4">
#Html.LabelFor(model => model.ConfigurationVariables[i].Name, new { #class = "form-control" })
</div>
</div>
<div class="row">
<div class="col-xs-4">
#Html.TextBoxFor(model => model.ConfigurationVariables[i].Value, new { #class = "form-control" })
</div>
</div>
}
This will be a complicated form and I can't just put every collection into the ActionResult parameters. Any help would be greatly appreciated
You need to hold the Name property in a hidden input so that it's submitted. Label values are lost.
#Html.HiddenFor(model => model.ConfigurationVariables[i].Name)
Alright, based on your comment you won't be able to utilize mvc's form binding. No worries.
Instead of this controller definition:
public ActionResult Submit(List<ConfigurationVariable> variables)
Use one of these two:
public ActionResult Submit()
public ActionResult Submit(FormCollection submittedForm)
In the first you can access the Request object, you'll need to debug and locate your variable, then you'll need some logic to parse it apart and used the values submitted.
In the second, the form collection will be made up of all the INPUT elements in your form. You will be able to parse through them directly on the object without interference from the other attributes of the Request object.
In both cases you will probably need to use #Html.TextBox, and not TextBoxFor, and you will need to dynamically populate your dropdowns in your view.
I'm not 100% sure about the Request object, but for sure on the FormCollection you will need to create an Input element for each value/collection you want submitted. Including hidden inputs for your textboxes
Your textboxes will need to be SelectListItem collections. those require a key and a value, and when they are submitted you can loop through the collection and check the .Selected attribute.
I would try with FormCollection first, and if that doesn't work fall back to the Request object.
Also note: you are not getting a viewmodel back from the form submission, you will need to rebuild it from the form elements. If you want to post prepopulated data to the view you will need to build a view model and do appropriate parsing on the view to display it.

Passing data to a Controller method via POST

I have a 'Survey' page which is declared as follows:
#using (Html.BeginForm("Survey", "Home", new { questionList = Model.Questions }, FormMethod.Post))
{
<div class="survey">
<ol class="questions">
#foreach (Question q in Model.Questions)
{
<li class="question" id="#q.QuestionName">
#q.QuestionText<br />
#foreach (Answer a in q.Answers)
{
<input class="answer" id="#a.DisplayName" type="checkbox" /><label for="#a.DisplayName">#a.AnswerText</label>
if (a.Expandable)
{
<input type="text" id="#a.DisplayNameFreeEntry" maxlength="250" /> <span>(250 characters max)</span>
}
<br />
}
</li>
}
</ol>
</div>
<div class="buttons">
<input type="submit" value="Finish" />
</div>
}
When I'm stepping through my code, it hits the method I've set up to process their survey:
[HttpPost]
public ActionResult Survey( List<Question> questionList, FormCollection postData)
{
//Process Survey
}
However, when I step through I am finding that the variable questionList is null and the variable postData does not contain any data from the Form. Trying to access checkboxes via Request[a.Displayname also does not work.
Everything I've read indicates that this is the correct way to persist values from the Model to the submission method, and that I should be able to access the FormCollection this way.
What am I doing wrong?
You have to save questionList as a hidden field on the page. Non-primitive types do not get persisted simply by passing them in.
One way you can do that is
#Html.HiddenFor(m => m.Foo)
Or you could do it directly in HTML like this
<input type="hidden" name="Var" value="foo">
where m is your model.
The fact that the postData is empty is weird, since every input element with id inside a form tag should be passed with the POST request.
But the questionList won't be received that way, since its a list of complex class (not just a string or int), and the default ModelBinder (the thing that turns the HTTP Request Variables into parameters passed to the action method) don't support lists of complex classes.
If you want to be able to receive List you will have to implement your own binding mechanism with CustomModelBinder.
This article can help you implement it.
One problem is your checkbox and your textbox are not properly bound to your model.
You should be using #Html.CheckBoxFor and #Html.TextBoxFor

PartialView form validation then calling a controller method in MVC3

I am very new to MVC3 and having problems wrapping my head around things. Right now I have a partial view which I have simplified below:
#model blah.blah.blah.blah.ForumPost
#using (Html.BeginForm()) {
<fieldset>
<legend>ForumPost</legend>
<div class="editor-label">
#Html.LabelFor(model => model.ForumID)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.ForumID)
#Html.ValidationMessageFor(model => model.ForumID)
</div>
<p>
<input type="submit" value="Create" />
#Html.ValidationSummary(true)
</p>
</fieldset>
<div>
#Html.ActionLink("Back to List", "Index")
</div>
}
I am not what to do for form validation. I have been trying to using jquery validation but I cant seem to find a good example that fits what I am doing and just get lost. I was basing it off this example here but it isn't enough.
After I am done I want to call a method in some code and I am not really sure of a clean way to do this. The way I have it currently working is using an ajax call and it's really ugly. Also a colleague suggested I pass the method an actual forum post but I don't know how. The code for the method I want to call is below:
public void PostToForum(ForumPost post)
{
UserService cu = new UserService();
int PostUserID = cu.GetUserIDByUsername(base.User.Identity.Name);
if (this.ModelState.IsValid)
{
ForumPost nfp = service.CreateForumPost(post);
}
}
Anyone have some tips? Thanks.
I can provide more code if it's necessary.
Html forms are usually submitted to controller actions:
[HttpPost]
public ActionResult Create(ForumPost model)
{
if (!ModelState.IsValid)
{
// validation failed => redisplay the view so that the user can fix the errors
return View(model);
}
// at this stage the model is valid => process it:
service.CreateForumPost(model);
return ...
}
Now since this is a partial view you must be careful with the view you are returning from this controller action as well as the model. If you don't use AJAX you should return the entire parent view and the parent view model. If you use an AjaxForm then you could only work with the partial model and view. Also in this case in the event of success you could return a Json result to the view to indicate this success so that the javascript handler that will be executed could take the respective actions.

Categories

Resources