How to render partial Views Sections in main layout file - c#

I am using asp.net MVC 5.
I have a _layout file as follows:
<!DOCTYPE html>
<html lang="en">
<head>
#Html.Partial("_Head")
#RenderSection("styles", required: false)
</head>
<body>
#RenderBody()
#RenderSection("scripts", required: false)
</body>
</html>
I then have my main view:
#{
Layout = "~/Views/Shared/_Layout.cshtml";
Title = "mainView"
}
<div class="partialcontents" data-url="/user/UserList"></div>
$(document).ready(function (e) {
$(".partialcontents").each(
function (index, item) {
var url = $(item).data("url");
if (url && url.length > 0) {
$(item).load(url, function () { $(this).css("background-image", "none"); });
}
}
);
});
and here is my partial view:
#{
}
<p> Partial View</p>
#section scripts
{
#Scripts.Render("~/bundles/script/datatable")
}
My controller that renders the partial view:
[Authorize]
public ActionResult UserList(UsersViewModel model, string sortOrder, string currentFilter, string searchString, int? page)
{
.... removed for simplicity
return PartialView("_UserList", new UsersViewModel()
{
Users = users.ToPagedList(pageNumber, pageSize)
});
}
So now my issue is that the section from the partial view is not being rendered in the main layout file. I understand why I think...
Its because I am rendering it in another controller. and then passing it along.
But how can I get this right?

You should place:
#section scripts
{
#Scripts.Render("~/bundles/script/datatable")
}
in the main view, because AJAX requests return only partial without layout so MVC doesn't know where it should render this script section. In this AJAX request #RenderSection("scripts", required: false) is never evaluated. If you omit #section scripts in your partial, it would probably work, but this is a not good approach to include JS in AJAX responses, so don't do that if you don't need too.

Related

TempData still available after subsequent http requests?

My textbook says that "TempData gets destroyed immediately after it’s used in subsequent HTTP request", so I write a simple test to verify
below is my code:
// SimpleForm.cshtml is just a simple view that uses a form to send post request to ReceiveForm action method
//Result.cshtml is just a simple view that products an output
public class HomeController : Controller
{
public ViewResult Index() => View("SimpleForm");
[HttpPost]
public RedirectToActionResult ReceiveForm(string name, string city)
{
TempData["name"] = name;
TempData["city"] = city;
return RedirectToAction(nameof(Transfer));
}
public RedirectToActionResult Transfer()
{
string name = TempData["name"] as string;
string city = TempData["city"] as string;
return RedirectToAction(nameof(Data));
}
public ViewResult Data()
{
string name = TempData["name"] as string;
string city = TempData["city"] as string;
return View("Result", $"{name} lives in {city}");
}
}
so when the application runs, it goes to Index() action method first, I fill up the form with name and city and press submit button, then it goes to ReceiveForm() action method, which setup TempData and redirect to Transfer() action method.
In the Transfer() action method, I read TempData, so TempData should get destroyed and unavailable to read in the next http request according to the textbook.
But in the Data(), I find that I can still read TempData, see the screenshot below:
and I checked the chrome dev tool, there was one post request and two get requests, which is all good and correct. so when does TempData actually get destroyed ?
additional code:
SimpleForm.cshtml:
#{ Layout = null; }
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Controllers and Actions</title>
<link rel="stylesheet" asp-href-include="lib/bootstrap/dist/css/*.min.css" />
</head>
<body class="m-1 p-1">
<form method="post" asp-action="ReceiveForm">
<div class="form-group">
<label for="name">Name:</label>
<input class="form-control" name="name" />
</div>
<div class="form-group">
<label for="name">City:</label>
<input class="form-control" name="city" />
</div>
<button class="btn btn-primary center-block" type="submit">Submit</button>
</form>
</body>
</html>
Result.cshtml:
#model string
#{ Layout = null; }
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Controllers and Actions</title>
<link rel="stylesheet" asp-href-include="lib/bootstrap/dist/css/*.min.css" />
</head>
<body class="m-1 p-1">
Model Data: #Model
</body>
</html>
For your scenario, this is caused by RedirectToActionResult. For RedirectToActionResult, which is IKeepTempDataResult.
public class RedirectToActionResult : ActionResult, IKeepTempDataResult
SaveTempDataFilter is filter that saves temp data. It will call SaveTempData.
private static void SaveTempData(
IActionResult result,
ITempDataDictionaryFactory factory,
IList<IFilterMetadata> filters,
HttpContext httpContext)
{
var tempData = factory.GetTempData(httpContext);
for (var i = 0; i < filters.Count; i++)
{
if (filters[i] is ISaveTempDataCallback callback)
{
callback.OnTempDataSaving(tempData);
}
}
if (result is IKeepTempDataResult)
{
tempData.Keep();
}
tempData.Save();
}
For SaveTempData, it will check whether IActionResult result is IKeepTempDataResult. If it is, it will keep the tempData.
If you want to avoid keep tempData between request, you could change RedirectToAction to LocalRedirect like
public IActionResult Transfer()
{
string name = TempData["name"] as string;
string city = TempData["city"] as string;
return LocalRedirect("~/Home/Data");
//return RedirectToAction(nameof(Data));
}

Cant find page after POST redirect C#

I go to a View, submit data via POST, but the redirect cannot find the Controller method. What am I doing wrong here? After submitting the form I get:
404 error: cannot find page. URL is: http://localhost:52008/InternalController/UpdateCardFormPost
Snippet from InternalController.cs:
public ActionResult UpdateCardFormView()
{
var CardToUpdate = new CardView();
return View(CardToUpdate);//return implementation of Cards.cshtml with the empty model that was passed to it
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult UpdateCardFormPost(CardView c)
{
CardModelIO.WriteCard(c);//#TODO: IMPLEMENT
return View("CardDetailView", c);
}
UpdateCardFormView.cshtml (the view with the form I am submitting):
#using LeanKit.API.Client.Library.TransferObjects
#model CardView
<!DOCTYPE html>
<html>
<!--Form used to change a card
STARTING DISPLAY called by in Internal/UpdateCardFormView
ENDING DISPLAY (post) called by UpdateCardForm in InternalController a specified below-->
<head>
</head>
<body>
#Html.BeginForm("UpdateCardFormPost", "InternalController", FormMethod.Post)
#Html.TextBoxFor(c => c.AssignedUserName);
<input type="submit" value="Submit Changes" />
</body>
</html>
Heres the CardDetailView.cshtml (the view I should be redirected to):
#using LeanKit.API.Client.Library.TransferObjects
#model IEnumerable<CardView>
<!--used for displaying an individual card in detail view
referenced in UpdateCardFormPost() method of Internal controller-->
<!DOCTYPE html>
<html>
<head>
</head>
<body>
CardView j = Model;
<p>j.AssignedUserId</p>
</body>
</html>
You've specified the controller name as InternalController but it's probably just called "Internal".
Try changing
#Html.BeginForm("UpdateCardFormPost", "InternalController", FormMethod.Post)
to
#Html.BeginForm("UpdateCardFormPost", "Internal", FormMethod.Post)
you are missing closing form tag
you should do it like
using (#Html.BeginForm("UpdateCardFormPost", "InternalController", FormMethod.Post))
{
...
}
#using(Html.BeginForm())
{
#Html.TextBoxFor(c => c.AssignedUserName);
<input type="submit" value="Submit Changes" />
}

sending integer value from controller to view in asp.net mvc

i am trying to send integer value from controller to view and display in view multiplied by 0.2
here the code in controller
public ActionResult Details()
{
ViewBag["salary"] = 1400;
return View();
}
and the code in view
#{
Layout = null;
int Salary=int.Parse(#ViewBag["salary"]);
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Details</title>
</head>
<body>
<div>
<p> Salary: </p>
#(Salary / 0.2)
</div>
</body>
</html>
but it throw an exception
An exception of type 'Microsoft.CSharp.RuntimeBinder.RuntimeBinderException' occurred in
System.Core.dll but was not handled in user code
Additional information: Cannot apply indexing with [] to an expression
of type 'System.Dynamic.DynamicObject'
Use this code in your controller:
ViewBag.salary = 1400;
And this code in your view:
int Salary=(int)ViewBag.salary;
Using the ViewBag is typically considered poor practice, I'd recommend using a Model (Model-View-Controller). I'll also mention that adding logic in the view is also poor practice, your logic should be controlled by the Controller.
Model
public class DetailsVM
{
public decimal Salary { get; set ; }
}
Controller (method)
public ActionResult Details()
{
var model = new DetailsVM();
model.Salary = 1400 / 0.2;
return View(model);
}
View
#model DetailsVM
#{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Details</title>
</head>
<body>
<div>
<p> Salary: </p>
#Model.Salary
</div>
</body>
</html>

From as partial view in layout page. MVC 5

I have a Partial View in my Layout page which contain a Form. The partial view open as popup. After submitting form I have to return back to the current page with opened popup and a success message should appear there but I am not able to return back to the right view.
My Layout Page
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>#ViewBag.Title || Midas WebSite</title>
#Styles.Render("~/Content/css")
#Scripts.Render("~/bundles/jquery")
#Scripts.Render("~/bundles/jqueryui")
#Scripts.Render("~/bundles/site")
#Scripts.Render("~/bundles/modernizr")
<script type="text/javascript" language="javascript">
/* Superior Web Systems */
function displayHideBox(boxNumber) {
if (document.getElementById("LightBox" + boxNumber).style.display == "none") {
$("#LightBox" + boxNumber).fadeIn();
$("#grayBG").fadeIn();
} else {
$("#LightBox" + boxNumber).fadeOut();
$("#grayBG").fadeOut();
}
}
</script>
</head>
<body>
<div id="grayBG" class="grayBox" style="display:none;"></div>
<div id="LightBox1" class="box_content" style="display:none;">
<div onclick="displayHideBox('1'); return false;" class="close">X</div>
#Html.Action("ActionToBindPartialView", "ControllerName")
</div>
#RenderBody()
#RenderSection("scripts", required: false)
#Scripts.Render("~/bundles/jqueryval")
My Partial View
<div id="divActions">
// My Form Here
#Ajax.ActionLink("Submit", "ActionName", "ControllerName", new AjaxOptions
{
HttpMethod = "POST",
UpdateTargetId = "divActions",
OnSuccess = "onSucess()"
})
</div>
Now my action code which I have tried so far:
1.
// My Code
return PartialView("MyPartialView");
2
// My Code
return PartialView("MyPartialView", MyModel);
3
// My Code
return View();
4
// My Code
return ParialView();
I am a beginner and not able to find what I am doing wrong. Please suggest what should I do to make this work.
It is hard to say what you are doing wrong, due to the limited workspace that we have to discuss the problem. However here is a working example that may help you, please follow each step:
Bundles
bundles.Add(new ScriptBundle("~/bundles/jquery")
.Include("~/Scripts/jquery-{version}.js"));
bundles.Add(new ScriptBundle("~/bundles/jqueryval")
.Include(
"~/Scripts/jquery.unobtrusive*",
"~/Scripts/jquery.validate*"));
HomeController
public ActionResult Index()
{
return View();
}
public PartialViewResult GetPartial()
{
return PartialView("_MyPartial");
}
Index.cshtml
<h2>Index</h2>
<div id="divActions">
#Ajax.ActionLink("Submit", "GetPartial", "Home", new AjaxOptions
{
HttpMethod = "POST",
UpdateTargetId = "divActions",
OnSuccess="onSuccess()"
})
</div>
<script type="text/javascript">
function onSuccess()
{
alert("Success!");
}
</script>
_MyPartial.cshtml
<h2>_MyPartial</h2>
_Layout.cshtml
<!DOCTYPE html>
<html>
<head>
<title>#ViewBag.Title</title>
#Styles.Render("~/Content/css")
#Scripts.Render("~/bundles/modernizr")
</head>
<body>
#RenderBody()
#Scripts.Render("~/bundles/jquery")
#Scripts.Render("~/bundles/jqueryval")
#RenderSection("scripts", required: false)
</body>
</html>
To confirm the rendered scripts in the page source (in the browser):
<script src="/Scripts/jquery-1.8.2.js"></script>
<script src="/Scripts/jquery.unobtrusive-ajax.js"></script>
<script src="/Scripts/jquery.validate.js"></script>
<script src="/Scripts/jquery.validate.unobtrusive.js"></script>
As you can see, there is nothing hard about it. You just need to follow the steps above.
I hope it helps.
Hint: check for other scripts errors, maybe your custom code to show the popup is causing a error. Double check if you are rendering javascript inside the partial view. Consider extract it to the regular view.

MVC Ajax form with unobstructive validation errors not going away

I am having an issue where form errors are persisting on the page even after the form is valid.
I have the following setup:
_Layout.cshtml:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>#ViewBag.Title</title>
<link href="#Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
<script src="#Url.Content("~/Scripts/jquery-1.5.1.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/modernizr-1.7.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/MicrosoftAjax.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/MicrosoftMvcAjax.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script>
</head>
<body>
#RenderBody()
</body>
</html>
index.cshtml:
#model SimpleForm.Models.Name
#{
ViewBag.Title = "Form";
}
<script>
$().ready(function () {
$("#quote_form").submit(function () {
return true;
});
});
</script>
#using (Ajax.BeginForm("RunQuote", "Page", new AjaxOptions { UpdateTargetId = "quoteSummary" }, new { id = "quote_form", onreset = "return confirm('Clear all form data?')" }))
{
<input type="submit" value="Send" />
<fieldset>
<legend>Name </legend>
<dl>
<dt>First</dt>
<dd>#Html.TextAreaFor(m => m.first, new { onblur = "$('#quote_form').submit();" })</dd>
<dt>Last</dt>
<dd>#Html.TextAreaFor(m => m.last, new { onblur = "$('#quote_form').submit();" })</dd>
</dl>
</fieldset>
<div class="qMatrixSubText">
#Html.ValidationSummary()
#{
Html.ValidateFor(m => m.first);
Html.ValidateFor(m => m.last);
}
</div>
<div id="quoteSummary" class="quoteSummary">
</div>
}
FormController.cs
using System;
using System.Web.Mvc;
using SimpleForm.Models;
namespace SimpleForm.Controllers
{
public class FormController : Controller
{
//
// GET: /Form/
public ActionResult Index()
{
return View();
}
public String RunQuote(Name quote)
{
return "Success!";
}
}
}
Name.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;
namespace SimpleForm.Models
{
public class Name
{
[Required()]
public string first { get; set; }
public string last { get; set; }
}
}
The form populates fine, I see the error if I don't put anything into first and the form won't submit. After populating something into first I get the ajax call and then it posts "Success" to the div.
However, this is the problem, the error "the first field is required." still remains on the page. It's as if the ajax submit form is going through before the validation gets a chance to clear the error, and then it never gets the chance.
Any way to get the validation to still remove the errors?
The form is not being validated, because your onblur event is short-circuiting the submission process in jquery-unobtrusive-ajax.js.
Instead, change your onblur event for both fields to the following:
$(this).parents('form:first').find(':submit')[0].click();
This will invoke the jquery.unobtrusive-ajax.js script, causing validation to occur prior to the Ajax submission to the server.
UPDATE
When I looked back at your view, I saw that you are calling #Html.ValidateFor() for your properties. This creates HTML markup outside of the div created by #Html.ValidationSummary.
Because this markup is outside of where unobtrusive validation expects it, this markup is never overwritten, and will always remain. Remove the #Html.ValidateFor() lines. If you need to have the validation messages after your initial load, call validate() on document.ready to set your validation messages.
Because I haven't found a proper solution to this problem, I've decided to simply target the validation-summary-errors div and add the class display:none; to the li elements. This hides the validation errors that have otherwise not cleared before submitting the form.
$('#form_errors').children('div.validation-summary-errors').children('ul').children('li').css('display','none')
There has to be a better solution, but I've tried everything and can't figure out why my attempts to force validation don't work.

Categories

Resources