PartialView form validation then calling a controller method in MVC3 - c#

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.

Related

RedirectToAction works in controller but does not update the view and URL

I have a controller for Login/Register and related actions. Ever since I changed the view from the one I made with Html helpers to test, to the one that my UI designer provided with html, after login I'm not redirected to the index page.
At first I thought that the login form was not submitting but after inserting some Console debug codes I realized that in the controller I am being redirected to the Index action but the Index view is not being showed in my browser and the URL is not changing.
Some developers with the same problem said it was because of the AJAX buttons but I don't think my view has those as far as I know and I don't know how to make sure.
This is my AccountController Code and the actions in it. I removed redundant codes from it:
public ActionResult Index()
{
Console.WriteLine("Redirected To Index");
return View(model);
}
public ActionResult Login()
{
Console.WriteLine("Entered Login Get Action");
return View();
}
[HttpPost]
public ActionResult Login(LoginViewModel model)
{
Console.WriteLine("Entered Login Post Action");
if (ModelState.IsValid)
{
Console.WriteLine("Login Redirect Action");
return RedirectToAction("Index");
}
return View(model);
}
and this is the form in my Login View that is being submitted with redundant codes removed:
<form class="cozy" method="post">
#Html.TextBoxFor(model => model.Username)
#Html.PasswordFor(model => model.Password)
<input type="submit" name="login" value="Login"/>
</form>
After submitting, "Redirected To Index" is printed in the console along with the previous lines, but I don't get redirected to the actual page.
Thanks in advance for spending time to answer to my problem.
It appears the problem was with some js scripts in the view, preventing the redirect. I removed them and the code started working properly again. then I added those js references again and now the code works.
I don't know why and how it didn't before but this was what I did that made it work again.
I think that you missed action attribute on the form.
<form class="cozy" method="post" action="#Url.Action("Login","Account")">
#Html.TextBoxFor(model => model.Username)
#Html.PasswordFor(model => model.Password)
<input type="submit" name="login" value="Login"/>
</form>
That will call Login action with post method when you hit submit button on your form.
َAs suggested in comments, You can use Html.BeginForm razor helper instead of pure html code.
#using (Html.BeginForm("Login", "Account", FormMethod.Post, new { id = "myform", #class = "cozy", enctype = "multipart/form-data" })) {
#Html.TextBoxFor(model => model.Username)
#Html.PasswordFor(model => model.Password)
<input type="submit" name="login" value="Login"/>
}
Instead of using return View(model); try to provide the full path of the view:
public ActionResult Index()
{
Console.WriteLine("Redirected To Index");
return View("add actual path of the view", model);
//For Example
//return View("~/Views/Test/Login.cshtml", model);
}

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.

Adding a save button to the index page

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");
}

How to render a newsfeed in an asp.net mvc4 partial view

I have a new MVC4 project.
In my _Layout.cshtml I have the following:
<div class="container maincontent">
<div class="row">
<div class="span2 hidden-phone">
#*
In here is a RenderSection featured. This is declared in a section tag in Views\Home\Index.cshtml.
If this is static text for the whole site, don't render a section, just add it in.
You should also be able to use #Html.Partial("_LoginPartial") for example.
This _LoginPartial will need to be a cshtml in the Shared Views folder.
*#
#{ Html.RenderPartial("_NewsFeed"); }
</div>
<div class="span10">
#RenderBody()
</div>
</div>
</div>
My partial view is
<div class="row newsfeed">
NEWS FEED
#foreach (var item in ViewData["newsfeed"] as IEnumerable<NewsItem>)
{
<div class="span2 newsfeeditem">
<h3>#item.NewsTitle</h3>
<p>#item.NewsContent</p>
#Html.ActionLink("More", "NewsItem", "News", new {id=#item.Id}, null)
</div>
}
Is there a way I can make the partial view make the data call. Currently I have to do the following in my controller for every action:
ViewData["newsfeed"] = _db.NewsItems.OrderByDescending(u => u.DateAdded).Where(u => u.IsLive == true).Take(4);
return View(ViewData);
I come unstuck where I already pass in a model to a view as I cannot then pass this into it as well.
I know I am doing something wrong, just not sure what or where.
I just want to be able to make a render call in my _layout and then the partial view to know to collect the data and then render itself. Or have I got the wrong end of the stick? I suppose I am trying to use it like an ascx...
You should switch from using a RenderPartial to a RenderAction. This allows you to go through the pipeline again and produce an ActionResult just like a partial, but with server side code. For example:
#Html.RenderAction("Index", "NewsFeed");
Then you make a NewsFeedController and provide an Index action method:
public class NewsFeedController : Controller
{
public ActionResult Index()
{
var modelData = _db.NewsItems.OrderByDescending(...);
// Hook up or initialize _db here however you normally are doing it
return PartialView(modelData);
}
}
Then you simply have your CSHTML like a normal view in your Views/NewsFeed/Index.cshtml location.

MVC 4 Clicking a Button to get a Partial Page

I am creating a page with a search property. The user will enter a number in the search field and hit the submit button. I need to be able to call a controller method in order to run the search code.
For now I am just trying to hit a partial page to just get some functionality in there. Below is the code I have so far. As of now nothing happens when I click the button. I hear Ajax was something to use so I have been playing with that a little. I am still learning the framework so bear with me.
<div class="span6 roundedCorners">
<div class="row-fluid Title">
<div class="span6">
Search Area
</div>
</div>
<div class="row-fluid individual">
<div class="row-fluid">
<div class="span4"><span class="pull-right fieldDescription">Number</span></div>
<div class="span8"><input /></div>
</div>
<div class="row">
<div class="span12" id="adminSubmitButton">
#using (Ajax.BeginForm(new AjaxOptions { UpdateTargetId = "_adminDetails" }))
{
<button type="submit" class="btn btn-inverse">Submit</button>
}
</div>
</div>
</div>
Your form is empty, it only has a submit button. You will need to move your search button inside the form. Something like this:
Model
public class SearchModel
{
[Required]
public string Query { get; set; }
}
View
#model SearchModel
...
#using (Ajax.BeginForm(new AjaxOptions { UpdateTargetId = "_adminDetails" }))
{
#Html.EditorFor(m => m.Query)
#Html.ValidationMessageFor(m => m.Query)
<button type="submit" class="btn btn-inverse">Submit</button>
}
<div id="_adminDetails"></di>
Note: Make sure you have an element with id _adminDetails
Your controller will have to take the model and perform the search. Example:
Controller
[HttpPost]
public ActionResult Index(SearchModel model)
{
//do something with your model (perform search)
return View(results);
}
Alright, I have it partially working now. When i click the button I am able to call the controller method using:
function adminButton()
{
$("#adminDetails").load("#Url.Action("ControllerMethod", "ControllerName")");
}
The #Url.Action helper allows me to call the method which returns a partial view. This is a step in the right direction. But from what I am reading I should be able to use the following Url helper instead of the #Url.Action:
"#Url("ControllerMethod", "ControllerName")/" + submitButton.Value
I have tried some variations and I am still doing some reading to figure out a solution. Is there a helper that would let me do this?
Thanks for the help guys. I did end up getting my problem solved friday evening. Basically what I did was load the partial page when the button was clicked through the help of JS/jQuery and the Razor #Url.Action() to call the controller method that returns a PartialView:
<script type="text/javascript">
function adminButton()
{
var link = "#Url.Action("ControllerMethod", "ControllerClass", new {
number = -1})";
var num = parseInt($("#number").val());
link = link.replace(-1, num);
$("#details").load(link);
}
</script>

Categories

Resources