How to set an action for a MVC forum? - asp.net-mvc

I want to set an action of a form when the user submits it. Instead of it reloading the current page I want to direct it to a controller to handle the request. How would I do this using the Razor engine within the the view.
<h2>#Html.DisplayFor(model => model.Title)</h2>
<p>#Html.Markdown(Model.Body)</p>
#if (Request.IsAuthenticated)
{
using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
#Html.HiddenFor(model => model.Id)
{
Model.ReplyId = ViewBag.Thread;
Model.Body = "";
}
#Html.HiddenFor(m => m.ReplyId);
<div class="form-horizontal">
<h4>Reply</h4>
<hr />
#Html.ValidationSummary(true)
<div class="form-group">
<div class="col-md-10">
#Html.EditorFor(m => m.Body, "Markdown")
<div id="mdFormatted"></div>
</div>
</div>
<div><input type="submit" value="Reply" class="btn btn-default" /></div>
</div>
}
}
else
{
Response.Redirect(Url.Action("Index"));
}

You can do that like so:
#using (Html.BeginForm("action", "controller"))
{
}
There is also an overload to specify whether the form should be sent via GET or POST:
#using (Html.BeginForm("action", "controller", FormMethod.Post))
{
}
As a separate aside, you're not really following the MVC pattern with the flow of the rest of your code. Specifically, you shouldn't be doing any redirecting to different views from within a view itself, because that's exactly what a controller is responsible for. In your example, it would be better to do something like this:
public ActionResult YourAction()
{
if (!Request.IsAuthenticated)
return RedirectToAction("Index");
return View();
}
This allows you to move logic, which shouldn't be in the view, to the controller, which both simplifies your view and enforces the MVC pattern. One other note would be to look into using AuthorizeAttribute if you're redirecting the user to a login action.

Related

Form submit using partail View error

I have created a form for add comment. I have created it in main View called Index in Home controller. bellow is Index View
private projectDBEntities db = new projectDBEntities();
public ActionResult Index()
{
ViewBag.Message = "Modify this template to jump-start your ASP.NET MVC application.";
return View();
}
public ActionResult AddComment()
{
return PartialView("_Comment");
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult AddComment(comment cmt)
{
if (ModelState.IsValid)
{
db.comments.Add(cmt);
db.SaveChanges();
return RedirectToAction("Index", "Home");
}
return PartialView("_Comment", cmt);
}
below is _Comment Partial View
#using (Html.BeginForm()) {#Html.AntiForgeryToken()#Html.ValidationSummary(true)
<fieldset>
<legend>comment</legend>
<div class="editor-label">
#Html.LabelFor(model => model.cmd_content)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.cmd_content)
#Html.ValidationMessageFor(model => model.cmd_content)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.t_email)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.t_email)
#Html.ValidationMessageFor(model => model.t_email)
</div>
<p>
<input type="submit" value="Comment" />
</p>
</fieldset>}
System.Web.HttpException was occuerd. I want to know What is the reason behind this error and what is the best method to form submit using partail View.
Id have to agree with Sandip's answer here, but to elaborate ..
#using (Html.BeginForm("AddComment", "Home"))
{
//Content to send to the server-side
}
As long as your model in your partial view points to your 'Comment' object, then this should work fine when using #Html.EditorFor(). In your controller, your already waiting for your 'Comment' object to be populated. So on Post, when the submit is clicked, it will populate that object.
Hope this helps.
#using (Html.BeginForm("AddComment", "Home"))
{
}

MVC Data Model from URL to Controller

Hi i need to pass some data from an URL to a controller in MVC. The data are in #Value = #Request.QueryString["rt"] in this code:
#using (Html.BeginForm("ResetPasswordToken", "Account")){
#Html.AntiForgeryToken()
#Html.ValidationSummary(true, "Reset Password non riuscito")
<div class="container above-footer login-form">
<div class="col-md-6" align="center" style=" margin-left:25%; margin-top:100px; margin-bottom:100px;">
<div class="editor-label">
#Html.LabelFor(m => m.ResetToken)
</div>
<div class="editor-field">
#Html.TextBoxFor(m => m.ResetToken, new { #Value = #Request.QueryString["rt"] })
#Html.ValidationMessageFor(m => m.ResetToken)
</div>
And i need to retrieve this value in the AccountController when i go on the submit button associated to this view. What is the best mode to do this without see it in the page?
I know is a very simple question but i need to do only this modification for tomorrow and i am very busy. Thanks to all and sorry for the question
In the controller action that rendered this view (the GET action):
model.ResetToken = Request["rt"];
return View(model);
and then in the view simply:
#Html.TextBoxFor(m => m.ResetToken)
or if you don't want this to be shown in the form you could also use a hidden field:
#Html.HiddenFor(m => m.ResetToken)
Now when the form is submitted back to the ResetPasswordToken action you can read this value from the model:
[HttpPost]
public ActionResult ResetPasswordToken(MyViewModel model)
{
// you can use the model.ResetToken property here to read the value
}
Alternatively you could include this value as part of the url when generating the action attribute of your HTML form:
#using (Html.BeginForm("ResetPasswordToken", "Account", new { ResetToken = Request["rt"] }))
{
...
}

MVC4 link between view and controller

I'm writing my first MVC app (MVC4). A lot of it's been generated using the excellent Razor tools in VS2010, but now I've run into a wall and I feel I don't understand something fundamental.
In my app,
a User can have access to many Clients.
I have a Client Controller and View.
I have a User Controller and View (expanded version of the simple
membership's UserProfile, populated using User.Identity).
What I want to do is let the user edit a client's details and ALSO edit their own account details on the same page.
I've tried using Html.Partial to add the UserProfile view to the Client view, but it bombs out trying to pass the UserProfile view the Client model.
How do I make the Client view call the UserProfile controller so it can read the User.Identity and return the correct UserProfile view?
I tried making it a viewmodel with both the client and userprofile classes attached, and doing it all on the one screen, but it refused to send values to the controller on submit.
Client view:
#model MyProject.Client
<div>
#Html.Partial("_AcctDetailsPartial")
</div>
<div>
#using (Html.BeginForm())
{
.
.
.
#Html.HiddenFor(model => model.ClientID)
{
.
.
.
<client fields>
{
.
.
.
<div class="form-actions">
<button type="submit" name="ButtonName" value="Company" class="btn blue"><i class="icon-ok"></i> Save</button>
<button type="button" class="btn">Cancel</button>
</div>
}
</div>
UserProfile View:
#model Surecall.UserProfile
<div style="height: auto;" id="accordion1-1" class="accordion collapse">
#using (Html.BeginForm())
{
<h3 class="form-section">Personal Info</h3>
<label class="control-label">First Name</label>
#Html.TextBoxFor(m => m.FirstName, new { #class = "m-wrap span8", #id="FirstName" })
<label class="control-label">Last Name</label>
#Html.TextBoxFor(m => m.Surname, new { #class = "m-wrap span8" })
<label class="control-label">Phone Number</label>
#Html.TextBoxFor(m => m.Mobile, new { #class = "m-wrap span8" })
<label class="control-label">Email</label>
#Html.TextBoxFor(m => m.Email, new { #class = "m-wrap span8" })
<label class="control-label">Position</label>
#Html.TextBoxFor(m => m.Occupation, new { #class = "m-wrap span8" })
<label class="control-label">Interests</label>
#Html.TextBoxFor(m => m.Interests, new { #class = "m-wrap span8" })
<div class="form-actions">
<button type="submit" name="SaveLoginDetails" value="SaveUser" class="btn green"><i class="icon-ok"></i> Save</button>
<button type="button" class="btn">Cancel</button>
</div>
}
</div>
What you are looking for is a Viewmodel.
Right now, your controller serves back a view consisting of the "Client" class/model. But you want a view consisting of Client + UserProfile.
I am not considering unut of work, repository and similar patterns. What you first need is to create the viewmodel
public class ClientUserProfileVM()
{
public Client client {get; set; }
public UserProfile user { get; set; } //use actual Usermodel here
}
In your controller write something like this
public ActionResult GetClientAndUser()
{
ClientUserProfileVM viewmodel = here comes some LINQ magic
select new ClientUserProfileVM {
client,
user
};
return View(viewmodel);
}
This way you are giving a viewmodel to your view consiting of a client and a user, which you can access with #model.user or #model.client in your view
There are plenty of interseting links for this:
ViewModel Best Practices
What is ViewModel in MVC?
http://www.youtube.com/watch?v=AkptHlDSKSQ

How to use ViewBag to pass list of data from View to controller

how can i sen a list of items from a view to a controller to save it. i believe that i can use Viewbag but i dont realy no how to use ite to pass data from view to controller.
this is what i have tried
My view
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>ProductionOrderItem</legend>
<div class="editor-label">
#Html.Label("ProducrionOrderNo");
</div>
<div class="editor-field">
#Html.TextBox("ProductionOrderNo", ViewBag.ProductionOrder as int)
</div>
<div class="editor-label">
#Html.Label("OrderName")
</div>
<div class="editor-field">
#Html.TextBox("OrderName", ViewBag.ProductionOrder as string)
</div>
<div class="editor-label">
#Html.Label("OrderDate")
</div>
<div class="editor-field">
#Html.TextBox("OrderDate", ViewBag.ProductionOrder as DateTime)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
and my controller
[HttpPost]
public ActionResult Create(FormCollection collection)
{
ProductionRegistration pr = new ProductionRegistration();
ProductionItem poi = new ProductionItem();
poi = Viewbag.ProductionOrder;
pr.SaveOrder(Conn, poi);
return RedirectToAction("Index");
}
You can't pass data from ViewBag/ViewData to the controller. It's one-way only (controller to view). The only way to get data back to the controller is to post it (post-body) or send it along in a querystring.
In fact, you should really avoid ViewBag as much as possible. It was added as a convenience and like most convenience methods, it's abused more often than not. Use a view model to both pass data to the view and accept data back from a post.
You strongly-type your view with:
#model Namespace.For.My.OrderViewModel
Then, you can use the [Foo]For methods of Razor to build your fields in a strongly-typed way:
<div class="editor-label">
#Html.LabelFor(m => m.ProductionOrderNo);
</div>
<div class="editor-field">
#Html.TextBoxFor(m => m.ProductionOrderNo)
</div>
And finally in your post action, you accept the view model as a param:
[HttpPost]
public ActionResult Create(OrderViewModel model)
{
...
}
And let MVC's modelbinder wire up the posted data for you.
No more dynamics. Everything is strongly-typed end-to-end, so if something goes wrong, you'll know it at compile-time, not run-time.

MVC4 Razor Multiple strongly type partial view with action in one page

I have a requirement where I have Login and Register form in Homepage. I believe this quite a common scenario however I am having difficulty achieving this.
This Login and Register forms are two separate Strongly Type Partial View which is being used in Index view
Below is the controller for Register. I will skip the login since if I get this to work, the other should be similar.
Register Controller
//
// GET: /Account/Register
[AllowAnonymous]
public ActionResult Register()
{
return PartialView();
}
//
// POST: /Account/Register
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Register(RegisterModel registerModel)
{
if (ModelState.IsValid)
{
// Attempt to register the user
try
{
_webSecurity.CreateUserAndAccount(registerModel.Email, registerModel.Password,
new { registerModel.FirstName, registerModel.LastName, registerModel.Email });
_webSecurity.Login(registerModel.Email, registerModel.Password);
return RedirectToAction("Manage", "Account");
}
catch (MembershipCreateUserException e)
{
ModelState.AddModelError("", ErrorCodeToString(e.StatusCode));
}
}
// If we got this far, something failed, redisplay form
return View(registerModel);
}
Index Controller
//
// GET: /Home/
public ActionResult Index()
{
//
// If logedin redirect to profile page
// Else show home page view
//
ViewBag.Message = "Modify this template to jump-start your ASP.NET MVC application.";
if (Request.IsAuthenticated)
{
return RedirectToAction("Manage", "Account", new { id = HttpContext.User.Identity.Name });
}
return View();
}
Register View
#using System.Web.Optimization
#model BoilKu.Web.ViewModels.RegisterModel
#using (Html.BeginForm("Register","Account", FormMethod.Post)) {
#Html.AntiForgeryToken()
#Html.ValidationSummary()
<fieldset>
<legend>Registration Form</legend>
<ol>
<li>
#Html.LabelFor(m => m.FirstName)
#Html.TextBoxFor(m => m.FirstName)
</li>
...
... Omitted codes
...
<li>
#Html.LabelFor(m => m.Password)
#Html.PasswordFor(m => m.Password)
</li>
</ol>
<input type="submit" value="Register" />
</fieldset>
}
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
Index View
#model BoilKu.Web.ViewModels.HomeModel
#{
ViewBag.Title = "Home Page";
}
#{
Html.RenderAction("Login", "Account");
}
#{
Html.RenderAction("Register", "Account");
}
Now with the above code, I have managed to get the partial view to display on the homepage. However when I click "Register" after filling up the details it will automatically redirect to my Register page with the fields pre populated. This is not what I want. I expect the register to happened on the homepage and redirect to Profile page when it has successfully registered. How do I go about doing this? Thank you for reading and apology for the noobishe questions. I am still quite new to MVC.
Update
Changing the Register Controller return from PartialView() to View() will act according the above requirement. However it will embed the page into a page. (i.e. the top navigation will be duplicated. )
Anyone?
I think it is better to create a partial page (user control) for register view
_register.cshtml
(no change here, just removed the script section. Parent view will render necessary script)
#using System.Web.Optimization
#model BoilKu.Web.ViewModels.RegisterModel
#using (Html.BeginForm("Register","Account", FormMethod.Post)) {
#Html.AntiForgeryToken()
#Html.ValidationSummary()
<fieldset>
<legend>Registration Form</legend>
<ol>
<li>
#Html.LabelFor(m => m.FirstName)
#Html.TextBoxFor(m => m.FirstName)
</li>
...
... Omitted codes
...
<li>
#Html.LabelFor(m => m.Password)
#Html.PasswordFor(m => m.Password)
</li>
</ol>
<input type="submit" value="Register" />
</fieldset>
}
index.cshtml
inside the HomeModel, it needs to have a RegisterModel property
#model BoilKu.Web.ViewModels.HomeModel
#{
ViewBag.Title = "Home Page";
}
#{
Html.RenderAction("Login", "Account");
}
#{
#Html.Partial("register", Model.RegisterModel)
}
When I create a MVC application, i usually retrieve all the information that is required by the page and put it into the model. In this scenario, HomeModel contains RegisterModel details. Then inside the view, you can render partial page and just pass the model as an argument. Partial view doesn't have to go back to the controller.
Then in your Register page, you can partial render
#Html.Partial("register", Model.RegisterModel)

Resources