MVC3 Model In Layout As Partial - c#

I'm trying to utilize the MVC3 model validation in my project as of current, however I want to have a simple login section to show in the layout at all times if the user is not logged in. I have the majority of code in place, however the only thing I'm stuck on is how I can post the model back to the form for any validation messages that I produce and need to return.
Normally something like this will work:
public ActionResult Login()
{
return View();
}
[HttpPost]
public ActionResult Login(LoginModel)
{
if(ModelState.IsValid())
{
//Run Further checks & functions
//Upon successful login, retuns to somewhere (Just site index in this example)
return RedirectToAction("Index", "Site");
}
return View(model);
}
Now obviously this won't work as I can't return View(model); on the partial unless I just want the login form to be displayed, however I want it to post back to the page that I have been editing from. For example: I navigate to a certain page, contact us, and want to login now. I enter my details on the form that is always available and I enter my details. An error occurs (Incorrect password, incorrect login, account doesn't exist etc...) and I should be brought back to the contact page with the form still filled in with the details that I entered (except obviously password) with validation summary working etc...
Also, any forms on the page that the layout has rendered still need to work correctly (with models etc)
I'm open to suggestions on how to get this working by other means of submission/return however it would be ideal to have the MVC model validation working.
If anyone needs me to elaborate on anything said, feel free to comment. I'll be actively responding for a while.

you should create a partial view for login and instead of using "#Html.BeginForm" use #Html.AjaxBegin which submit your page by Ajax call and it RenderHtmlString of login view.
for e.g
public ActionResult Login(LoginModel)
{
if(ModelState.IsValid())
{
//Run Further checks & functions
//Upon successful login, retuns to somewhere (Just site index in this example)
return RedirectToAction("Index", "Site");
}
return RenderPartialViewToString("Login",model);
}
protected string RenderPartialViewToString(string viewName, object model)
{
if (string.IsNullOrEmpty(viewName))
viewName = ControllerContext.RouteData.GetRequiredString("action");
ViewData.Model = model;
using (StringWriter sw = new StringWriter())
{
ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView(ControllerContext, viewName);
ViewContext viewContext = new ViewContext(ControllerContext, viewResult.View, ViewData, TempData, sw);
viewResult.View.Render(viewContext, sw);
return sw.GetStringBuilder().ToString();
}
}
after adding "RenderPartialViewToString" method which will return you "RenderHtmlString" of your partial view. you must be pass viewName and Model as parameter to this Method.
in your partail View.
<div id="targetId">
</div>
#using(Ajax.BeginForm("Login",new AjaxOptions{ HttpMethod="POST", UpdateTargetId="targetId"}))
{
<input type="submit" value="save" />
}
Note: you must be pass UpdateTargetId there your result will Append.

See this question: How do I pass value to MVC3 master page ( _layout)?
There are complete guide what to do to pass your model to layout

Related

Execute a controller from view in ASP.NET MVC - Razor

I am trying to print the name of the user who executes my web app. It is written in MVC-Razor.
From the initial View, I would to execute the controller below:
[Authorize]
public ActionResult Check()
{
var check = new CheckAD();
var user = new User {Name = check.CheckSecurityWithAD()};
if (!string.IsNullOrEmpty(user.Name))
{
return View("Checked", user);
}
var errors = new ErrorsModel()
{
Messages = new List<string>(){"You don't have permission"}
};
return View("Error", errors);
}
This controller returns another view if the user is correctly authenticated:
#model UsersActivationWeb.Models.User
#{
ViewBag.Title = "Checked";
}
#{ <p> Logged come #Model.Name </p>};
How can I print the second view (I think it's a partial view) in the first one using the controller?
Thanks
Sounds to me like you need an Html.Action. This will run the controller code and display the view contents that are produced where you place the call.
Most likely you will need this overload, Html.Action(string actionName, string controllerName).
Assuming the controller is called CheckController. In your initial view call it like this
#Html.Action("Check","Check")
Since you don't want people navigating to the Check view you should give it a ChildActionOnly attribute so it looks like this
[Authorize]
[ChildActionOnly]
public ActionResult Check()
{
//rest of code
}
Finally you almost certainly don't want the layout contents to appear with the Checked view so change your Checked view to this
#model UsersActivationWeb.Models.User
#{
Layout = null;
}
#{ <p> Logged come #Model.Name </p>};
Since you are doing authorization logic in the Check action you might not need the Authorize attribute. I say that because with it a user not logged in will not get the error or their name. Maybe you want this though, I'd need to know more about your code to say for sure.
This way you will either get the name of the user or the errors as required.

Send Selected DropDownList value to HomeController ActionResult

Hi I have a drop down list that is filled in from comma delimited values in the config. This works fine.
What I am trying to do is to send the selected value on button click to a ActionResult in the HomeController.
I created a Model, which is taking a string. When I hit the button I get error:
The view 'TestAction' or its master was not found or no view engine supports the searched locations.
This is what my Controller looks like:
[HttpPost]
[ActionName("TestAction")]
public ActionResult TestAction(SQL_Blocks_App.Models.DropdownList SelectedValue)
{
//System.Diagnostics.Debug.WriteLine(SelectedValue);
return View();
}
This is what my model looks like:
public class DropdownList
{
//
// GET: /DropdownList/
[Display(Name = "Servers")]
public string SelectedValue{ get; set; }
}
and this is what my Index View looks like:
<form id="SelectedValue" action="/Home/TestAction" method="post" style="margin: 0">
<div class="col-lg-5">
#{
ViewBag.Title = "Index";
}
#Html.DropDownList("YourElementName", (IEnumerable<SelectListItem>)ViewBag.DropdownVals, "--Choose Your Value--", new
{
//size = "5",
style = "width: 600px"
})
</div>
<div class="col-lg-5">
<input type="submit" value="Run Query" />
<input id="Button2" type="button" value="Clear" onclick="window.location.reload()" />
</div>
</form>
I want to clarify. My end goal is to use the selected value in a SQL query in the ActionResult and return the results back to the index so I can fill them in a table. ( You don't have to show me how to do the SQL part for now I just would like to see the selected value at least printed in the output.)
Redirect to index action, and pass the parameters along
[HttpPost]
[ActionName("TestAction")]
public ActionResult TestAction(SQL_Blocks_App.Models.DropdownList _selectedValue)
{
//System.Diagnostics.Debug.WriteLine(SelectedValue);
return RedirectToAction("Index", "[Controller]", new {#_selectedValue = _selectedValue });
}
and then your Index method should accept the parameter.
[HttpGet]
public ActionResult Index(SQL_Blocks_App.Models.DropdownList _selectedValue)
{
//use _selectedValue
}
I would recommend using another method other than your index, or make Dropdownlist nullable/set a default for it.
The default framework behavior of return View() is to return a view with the same name as the currently-executing action. Which is TestAction. The error is telling you that no such view was found.
You have a couple of options. You can either create the view, or you can return something else. For example, if you want to redirect back to the Index then you can return a redirect result:
return RedirectToAction("Index");
You could also specify the Index view in the response:
return View("Index");
However, keep in mind that the URL will still be for TestAction and not for Index, which could result in unexpected changes to behavior if you're not aware of this.
Edit: Based on comments on this answer, it sounds like what you actually want is to build a pair of actions which generally operate on the same view. This isn't particularly common for an index view, but is very common for edit views. The only difference is semantics, structurally the concept works anywhere.
Consider two actions:
public ActionResult Index()
{
// just show the page
return View();
}
[HttpPost]
public ActionResult Index(SQL_Blocks_App.Models.DropdownList SelectedValue)
{
// receive data from the page
// perform some operation
// and show the page again
return View();
}
Requests between these two actions would differ only by the HTTP verb (GET or POST), not by the action name on the URL. That name would always be "Index". But when the form on the index view is submitted via POST and has a "SelectedValue", the second action is invoked instead of the first.
In that second action you would perform your database interaction, gather whatever data you needed, and if necessary include a model or some additional data in the response.
You TestAction method is returning to a View. Make sure View TestAction.cshtml exists and is in the Home folder.

ViewDataDictionary vs anonymous object in ASP.net MVC 5

I am a beginner and I am going through some tutorials in my MVC. So, I came across two scenarios.
Scenario 1.
I had to pass some data to my view on post and then send that data as hidden field. Here is the code.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult ForgotPassword(ForgotPasswordMV viewModel)
{
if (ModelState.IsValid)
{
return RedirectToAction("VerifyToken", new { emailId = viewModel.EmailId });
}
^^ USING ANONYMOUS OBJECTS
return View();
}
public ActionResult VerifyToken(string emailId = null)
{
VerifyTokenMV viewModel = new VerifyTokenMV
{
EmailId = emailId
};
return View(viewModel);
}
VerifyToken View
#using (#Html.BeginForm("VerifyToken", "Security"))
{
#Html.HiddenFor(m => m.EmailId)
<button class="btn btn-primary">Continue</button>
}
Works Perfectly fine. I am able to receive values of EmailId. So far so good.
Scenario 2.
Needed to open a partial view from Main view, here is the snippet.
Main cshtml file
<div class="abc">
#Html.Partial("../Widget/Customize", Model.Unit, new ViewDataDictionary() { { "ElementName", "UnitWidget" } })
</div>
partial cshtml file
#{
string custWidgetElementName = ViewBag.ElementName;
}
// some html code below
Observation:
In scenario 2 why have I used ViewDataDictionary. Although both example works perfectly fine. But is there any reason that I had to use ViewDataDictionary. In scenraio 1 can we use ViewDataDictionary? If Yes, then which one is optimum solution.
Question: When I need to pass values shall I use new {key : value} or use ViewDataDictionary or there is no corelation? Instead of ViewDataDictionary can I use anonymous object in Senario 2
Your two scenarios are totally different. They are not doing the same thing.
In scenario 1 when using this line:
return RedirectToAction("VerifyToken", new { emailId = viewModel.EmailId });
A new URL is genrated and sent back to the client (the browser) with HTTP Code 301 or 302. When received the browser will re-contact your application wiht the received URL. With that URL, your application will execute the associated action. In your case, the client's browser will call VerifyToken action with the emailId parameter setted when you call RedirectionToAction into ForgotPassword action. So using RedirectionToAction method is just telling that method to generate a new URL with parameter defined in the anonymous type.
In scenario 2 is completely different to scenario 1. With this line:
#Html.Partial("../Widget/Customize", Model.Unit, new ViewDataDictionary() { { "ElementName", "UnitWidget" } })
You're telling your view to inject the partial view which path is ../Widget/Customize. Because that partial view the strongly typed, you passed Model.Unit as an second parameter. You use also a third parameter new ViewDataDictionary() { { "ElementName", "UnitWidget" } } because your partial seems to internally need to access to the dynamic property ViewBag or dictionary property ViewData of your view.
Conclusion:
In scenario 1 you are just telling the client's browser to go to the new URL you have generated after requesting ForgetPassword URL. We just call that a rediretion.
In scenario 2, you're just rendering a partial view into a view. The client's broswer doesn't know anything what's going on with partial views they don't know if they exist.

Generate view without controller action

In my ASP.NET MVC project I have a controller that one action just result view. This Action does not get any argument and just return a CSHTML page. This page could not be partial.
Does anybody know a better way to generate view - I mean can I generate view without controller action?
//Edit - sample codes
Right now in my UserPanelController i have an action ChangeSettings
[HttpGet]
public ActionResult ChangeSettings()
{
return View("Configuration");
}
So if i want to get a configuration View i have to do request to controller from for example navigation:
<nav>
<div class="nav-wrapper">
Logo
<ul id="nav-mobile" class="right hide-on-med-and-down">
<li>Sass</li>
<li>Components</li>
<li>Konrad</li>
</ul>
</div>
</nav>
Can i get a ConfigurationView without my controller action?
Maybe I'm a bit too late but what you are probably looking for is this:
#{ Html.RenderPartial("_PartialViewName"); }
Notice that you can use a model in your view as well, by passing it directly from the view that is calling the partial one.
You can render a view yourself by calling the following method
private static string RenderPartialViewToString(Controller controller, string viewName, object model)
{
controller.ViewData.Model = model;
using (var sw = new StringWriter())
{
var viewResult = ViewEngines.Engines.FindPartialView(controller.ControllerContext, viewName);
var viewContext = new ViewContext(controller.ControllerContext, viewResult.View, controller.ViewData, controller.TempData, sw);
viewResult.View.Render(viewContext, sw);
return sw.ToString();
}
}
This will give you html string containing your rendered view. I've used this before to render HTML for emails where the email content changed from user to user.
Hope this helps.
If it is static content, you can add an .html file inside your project. The web server will serve it.
To make sure the MVC routing will not interfer you can add something like this to your routes:
routes.IgnoreRoute("{file}.html");

ASP MVC and changing url of posted form

I have an MVC 4 app and I am using a RESTful methodology for my URLs. I have the following routes registered in my app (along with others that are not relevant to my question:
//EDIT
routes.MapRoute(alias + "_EDIT", alias + "/{id}/edit",
new { controller = controllerName, action = "edit" },
new { httpMethod = new RestfulHttpMethodConstraint(HttpVerbs.Get) });
//PUT (update)
routes.MapRoute(alias + "_PUT", alias + "/{id}",
new { controller = controllerName, action = "update" },
new { httpMethod = new RestfulHttpMethodConstraint(HttpVerbs.Put) });
I have the following methos in my controller mapping to these routes:
public override ActionResult Edit(int id)
{...}
public override ActionResult Update(RequestEditViewModel userModel)
{
if (!ModelState.IsValid)
{
//do some stuff to ensure lookups are populated
...
return View("Edit", userModel);
}
}
In my app when I perform a request to edit a request my URL looks like:
http://server/request/1/edit
it correctly calls the Edit method on my controller.
My Edit.cshtml uses the followng to ensure the Update method is called on PUT:
#using (Html.BeginForm("Update", "Request"))
{
#Html.HttpMethodOverride(HttpVerbs.Put);
...
}
My form is generated as follows:
<form action="/requests/71" method="post" autocomplete="off" novalidate="novalidate">
<input name="X-HTTP-Method-Override" type="hidden" value="PUT"/>
...
</form>
When I click the submit button it correctly calls my Update method.
OK...Now for the issue. If my model is NOT valid I want to return back the Edit model. As you can see in the above code but, the URL is the one called from the submit button:
http://server/request/1
not
http://server/requests/1/edit
I have tried an reviewed two other options but both of these redirect the request back through the Edit method again which adds additional overhead and also puts all the model values in the querystring which I do NOT want:
return RedirectToAction("Edit", userModel);
return RedirectToRoute("requests_Edit", userModel);
So, is there a way to just return the View as I have in my code but, ensure the URL changes back and include the "/edit"?
The only alternative I have come up with is to perform an AJAX call and put the update that way the URL never changes, but I was trying to avoid that for this form.
Conceptually, you want to be doing something like a Server.Transfer (that is, making on URL appear to be another.) This discussion may be of use to you:
How to simulate Server.Transfer in ASP.NET MVC?

Categories