How to get result of generated htmlstring from htmlhelper in controller action? - asp.net-mvc

I have a custom html helper in my App_code folder.
I want to call it from my action and get generated html string.
What should I do?
My MailTemplate.cshtml contents:
#{
Layout = null;
}
#helper ContactUs(string body,string name,string email)
{
<div style="direction:rtl;font-family:Tahoma;color:#3b7a09;">
<span>از طرف:</span><span>#name</span>
<br />
<span>ایمیل:</span><span>#email</span>
<br />
<div style="background-color:#f5dfb2;font-size:13px;">
#body
</div>
</div>
}

Check below code, it has example of html helper method and that helper method use in view.
HTML helper method in App_Code(App_Code\HelperMethods.cshtml)
#helper renderheader(WebViewPage page, string displayText, string ActionName)
{
<div class="row">
<div class="span4">
#page.Html.ActionLink(displayText, ActionName, new { id = 0 }, new { #class = "btn btn-small
btn-primary" })
</div>
<div class="span4">
#page.ViewBag.ErrMsg
</div>
</div>
}
Helper Method Call in View
#HelperMethods.renderheader(this, "AddGroup" , "Create")

Related

Partial View replaces Parent view

I'm working on web app developed in ASP.Net MVC, having a partial view which should be rendered inside its parent view.
Parent view has a HTML Dropdown, on-change event should bind respective data to partial view. But on selection change, the complete parent view is replaced with partial view (child view).
Parent View (Index.cshtml)
<h3>Please Select Group</h3>
#using (Html.BeginForm("EmployeeDeptHistory", "Home", FormMethod.Post))
{
#Html.AntiForgeryToken()
if (ViewBag.DepartmentList != null)
{
#Html.DropDownList("DepartmentName", ViewBag.DepartmentList as SelectList, "-- Select --", new { Class = "form-control", onchange = "this.form.submit();" })
}
}
<div>
#{Html.RenderPartial("_EmployeeDeptHistory");}
</div>
Partial View (_EmployeeDeptHistory.cshtml)
#model IEnumerable<PartialViewApplSol.Models.EmployeeDepartmentHistory>
#if (Model != null)
{
<h3>Employees Department History : #Model.Count()</h3>
foreach (var item in Model)
{
<div style="border:solid 1px #808080; margin-bottom:2%;">
<div class="row">
<div class="col-md-2">
<strong>Name</strong>
</div>
<div class="col-md-5">
<span>#item.Name</span>
</div>
</div>
<div class="row">
<div class="col-md-2">
<strong>Shift</strong>
</div>
<div class="col-md-5">
<span>#item.Shift</span>
</div>
</div>
<div class="row">
<div class="col-md-2">
<strong>Department</strong>
</div>
<div class="col-md-5">
<span>#item.Department</span>
</div>
</div>
<div class="row">
<div class="col-md-2">
<strong>Group Name</strong>
</div>
<div class="col-md-5">
<span>#item.GroupName</span>
</div>
</div>
<div class="row">
<div class="col-md-2">
<strong>Start Date</strong>
</div>
<div class="col-md-5">
<span>#item.StartDate</span>
</div>
</div>
<div class="row">
<div class="col-md-2">
<strong>End Date</strong>
</div>
<div class="col-md-5">
<span>#item.EndDate</span>
</div>
</div>
</div>
}
}
I think the possible mistake is returning partial-view on drop down selection changed.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult EmployeeDeptHistory(FormCollection form)
{
IEnumerable<EmployeeDepartmentHistory> empHistList;
using (IDbConnection con = new SqlConnection(connectionString))
{
empHistList = con.Query<EmployeeDepartmentHistory>("sp_StoredProc", new { DeptId = form["DepartmentName"] }, commandType: CommandType.StoredProcedure);
}
return View("_EmployeeDeptHistory", empHistList);
}
Instead of standard form submit, you need to use jQuery.ajax() function to load partial view inside HTML element without replacing parent view. Here are those steps:
1) Remove onchange event from DropDownList helper, and assign AJAX callback bound to change event:
View
#Html.DropDownList("DepartmentName", ViewBag.DepartmentList as SelectList, "-- Select --", new { #class = "form-control" })
jQuery (inside$(document).ready())
$('#DepartmentName').change(function () {
var selectedValue = $(this).val();
if (selectedValue && selectedValue != '')
{
$.ajax({
type: 'POST',
url: '#Url.Action("EmployeeDeptHistory", "ControllerName")',
data: { departmentName: selectedValue };
success: function (result) {
$('#targetElement').html(result); // assign rendered output to target element's ID
}
});
}
});
2) Remove FormCollection and use a string argument which has same name as AJAX callback argument, also make sure the action method returns PartialView:
Controller
[HttpPost]
public ActionResult EmployeeDeptHistory(string departmentName)
{
IEnumerable<EmployeeDepartmentHistory> empHistList;
using (IDbConnection con = new SqlConnection(connectionString))
{
empHistList = con.Query<EmployeeDepartmentHistory>("sp_StoredProc", new { DeptId = departmentName }, commandType: CommandType.StoredProcedure);
}
return PartialView("_EmployeeDeptHistory", empHistList);
}
3) Finally, don't forget to add ID for target element specified by AJAX callback's success part to load partial view:
View
<div id="targetElement">
#Html.Partial("_EmployeeDeptHistory")
</div>

Bad Request MVC HttpPost

I have the following cshtml file.
#model Models.AuthorizeUser
#{
ViewBag.Title = "Authorize";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<div class="container">
<div class="card card-container">
<img class="profile-img-card" src="~/Content/images/rblogo_reverse-pms348_000.gif" />
<p> </p>
<form method="POST">
<p>Hello, #Model.Name</p>
<p>A third party application want to do the following on your behalf:</p>
<ul>
#foreach (var scope in Model.Scopes)
{
<li>#scope.ScopeDescription</li>
}
</ul>
<div class ="row">
<div class="col-md-4">
<button class="btn btn-block btn-primary btn-signin" name="submit" type="submit" value="authorize">Grant</button>
</div>
<div class="col-md-8">
<button class="btn btn-block btn-primary btn-signin btn-small-text" name="submit" type="submit" value="logout">Sign in as different user</button>
</div>
</div>
</form>
</div>
</div>
I have my controller file as follows:
public class OAuthController : Controller
{
[HttpGet]
public ActionResult Authorize()
{
logger.Trace("Authorize method entered");
AuthorizeUser authorizeUser;
......
return View(authorizeUser);
}
[HttpPost]
public ActionResult Authorize(AuthorizeUser authorizeUser, string submit)
{
logger.Trace("Authorize with object");
if (Response.StatusCode != 200)
{
logger.Trace("status code " + Response.StatusCode);
logger.Trace("status description " + Response.StatusDescription);
return View("AuthorizeError");
}
..............
}
When the form is displayed, the info is displayed correctly. After I click Grant button, I got Response.StatusCode == 400. Both authorizeUser and submit are null. I am expecting StatuCode == 200 with values of authrizeUser and submit.
Have you tried using Html.BeginForm()?
Instead of using <form method="POST"> you could use Html.BeginForm("Action", "Controller")

Cannot apply indexing with [] to an expression of type 'HttpRequest'

I am trying to get a value from a textbox of my View.
This is my View:
#model MyDataIndexViewModel
#{
<div class="row">
<div class="col-xs-12 col-sm-12 col-md-12">
<h1>Meine Daten</h1>
</div>
</div>
var item = Model.User;
<div class="row">
<div class="col-xs-6 col-sm-6 col-md-6 myDataTitle">Email</div>
<div class="col-xs-6 col-sm-6 col-md-6">
#Html.TextBox("txtEmail", "", new { placeholder = item.Email})
</div>
</div>
}
<div class="row">
<div class="col-xs-12 col-sm-12 col-md-12">
<a class="btn btn-default pull-right" href="/ChangeMyData/Save">Speichern</a>
</div>
</div>
This is my Controller:
[HttpPost]
public ActionResult Save()
{
var email = Request["txtEmail"].ToString();
return View();
}
I get the error just as it says in the Title.
Thank you in advance!
VIEW:
#model MyDataIndexViewModel
#using (Html.BeginForm("Save", "CONTROLLER_NAME"))
{
<div class="row">
<div class="col-xs-12 col-sm-12 col-md-12">
<h1>Meine Daten</h1>
</div>
</div>
var item = Model.User;
<div class="row">
<div class="col-xs-6 col-sm-6 col-md-6 myDataTitle">Email</div>
<div class="col-xs-6 col-sm-6 col-md-6">
#Html.TextBox("txtEmail", "", new { placeholder = item.Email, id="txtEmail"})
</div>
</div>
<div class="row">
<div class="col-xs-12 col-sm-12 col-md-12">
<a class="submit btn btn-default pull-right">Speichern</a>
</div>
</div>
}
CONTROLLER
[HttpPost]
public ActionResult Save()
{
var email = Request.Form["txtEmail"].ToString();
return View();
}
You can either use strongly-typed viewmodel binding:
View
#model MyDataIndexViewModel
#* other stuff *#
#Html.TextBox("txtEmail", Model.Email)
Controller
[HttpPost]
public ActionResult Save(MyDataIndexViewModel model)
{
var email = model.Email;
return View();
}
Or use a TextBoxFor directly for model binding:
#model MyDataIndexViewModel
#* other stuff *#
#Html.TextBoxFor(model => model.Email)
Or if you still want to use HttpRequest members, Request.Form collection (a NameValueCollection) is available to retrieve text from txtEmail input:
[HttpPost]
public ActionResult Save()
{
var email = Request.Form["txtEmail"].ToString();
return View();
}
Note that Request["txtEmail"] is discouraged due to no compile time safety applied for it, because the key value may retrieved from Request.QueryString, Request.Form or other HttpRequestBase members.
Similar issue:
MVC TextBox with name specified not binding model on post
Access form data into controller using Request in ASP.NET MVC

PagedListPager page always null

This used to work... but now the >> anchor tag of the PagedListPager always passes null to the controller for the page value required...
VS 2013 Web Express & MVC 4 with latest package updates for all.
Just like in Scot Allen's MVC 4 intro, I have a partial view with a PagedListPager
The Controller:
public ActionResult Catalog(string Id= "0", int page=1)
{
var CurrentItemsPage = (get-data-blaw-blaw-blaw).ToPagedList(page,18);
var model = new ShowRoomCatalogPackage(){ CurrentItems = CurrentItemsPage};
return View(model);
}
The catalog page
#model craftstore.Models.ShowRoomCatalogPackage
#{
ViewBag.Title = "Catalog";
Layout = "~/Views/Shared/_Details.cshtml";
}
#using (Ajax.BeginForm("Catalog", "Home", new { category = #Model.SelectedCategoryId, page = 1 },
new AjaxOptions
{
UpdateTargetId = "products",
InsertionMode = InsertionMode.Replace,
HttpMethod = "post"
}
)
)
{
<div class="container" >
<div class="row">
<div class="col-lg-10 col-md-5 col-sm-4 dropdown-menu">
#Html.LabelFor(m => m.SelectedCategoryId)
#Html.DropDownList("id", Model.CategoryItems, new { #id = "ddlCategories", onchange = "this.form.submit();" })
</div>
<div class="col-lg-2 col-md-2 col-sm-1">
#Html.ActionLink("Your Cart", "Index", "ShoppingCart", "", new { #class = "btn btn-green btn-lg" })
</div>
</div>
<div class="row">
#Html.Partial("_CatalogPartial", Model.CurrentItems)
</div><!-- row -->
</div><!-- container -->
}
<br />
<br />
#section Scripts
{
<script type="text/javascript">
new AnimOnScroll(document.getElementById('grid'), {
minDuration: 0.4,
maxDuration: 0.7,
viewportFactor: 0.2
});
</script>
}
The partial view:
#model IPagedList<ShowroomCatalog>
<div id="productList">
<div class="col-lg-12 col-md-12 col-sm-12">
<div class="pagedList" data-cs-target="#productList">
#Html.PagedListPager(Model, page => Url.Action("Index", new { category = ViewBag.SelectedCategoryId, page }), PagedListRenderOptions.MinimalWithItemCountText)
</div>
<ul class="grid effect-2" id="grid">
#foreach(var item in Model)
{
var path = String.Format("~/Content/Images/catalog/{0}/{1}", item.OfferType, item.ImagePath);
<li>
<div class="itembox">
<div class="imagebox">
<a href="#Url.Action("Detail", "Home", new { id = item.Id })" title="Detail for #item.CatalogName">
<img class="catalogimg" src="#Url.Content(path)" />
</a>
</div>
<p>#item.CatalogName</p>
</div>
</li>
}
</ul>
</div>
</div><!-- productlist -->
Now the rendered partialview in the browser doesn't have anything in the anchors which may or may not be normal...
<div class="pagedList" data-cs-target="#productList">
<div class="pagination-container"><ul class="pagination"><li class="disabled PagedList-skipToPrevious"><a rel="prev">«</a></li><li class="disabled PagedList-pageCountAndLocation"><a>Showing items 1 through 18 of 65.</a></li><li class="PagedList-skipToNext">»</li></ul></div>
</div>
And when you hover on the >> it doesn't show the page parameter in the URL:
Again, back in the Controller - I get the category (15) but no page parameter or Request.URL parameter is passed to the controller - it's not hiding because of some routing mistake...I think...
How do I get the paging control to work again...???
[EDIT: one more note - the url path on the pager is /controller/action/category/page rather than what shows up on Scot Allen's OdeToFood example where it's equivalent would be /controller/action/category?page=n (like /Home/Catalog/15?page=1 ]
I was missing the JS for the PagedList class anchor element.
var getPage = function () {
var $a = $(this);
var options = {
url: $a.attr("href"),
data: $("form").serialize(),
type: "get"
};
$.ajax(options).done(function (data) {
var target = $a.parents("div.pagedList").attr("data-otf-target");
$(target).replaceWith(data);
});
return false;
};
And this is fired off by :
$(".main-content").on("click", ".pagedList a", getPage);
BUT, this means you need to have your #RenderBody() call in your _Layout.cshtml file wrapped in something with a class of main-content. An example:
<section class="content-wrapper main-content clear-fix">
#RenderBody()
</section>

Use MVC helpers inside jquery.tmpl templates

I have a simple foreach template and inside every element I want an ActionLink but that ActionLink needs to send an Id to edit the element.
The item to be templated:
<div data-bind="template: {
name: 'postsTemplate',
foreach: posts
}">
</div>
The template:
<script id="postsTemplate" type="text/html">
<h2 data-bind="text: Title"></h2>
<p class="post-info">
<p class="post-info" data-bind="text UserName"></p>
<span data-bind="Body"></span>
<p class="post-footer">
#Html.ActionLink("Comments", "IndividualPost", "Post", null, null, "comments", new {id = })
</p>
</p>
</script>
How can I send the actual post Id through the ActionLink? I mean, How I can access to the post's id without using data-bind? (Because it's a helper).
If you would implement your own ActionLink extension along the line of:
public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText,
string actionName, string controllerName,
object routeValues, bool noEncode)
{
var urlHelper = new UrlHelper(htmlHelper.ViewContext.RequestContext);
var url = urlHelper.Action(actionName, controllerName, routeValues);
if (noEncode) url = Uri.UnescapeDataString(url);
var tagBuilder = new TagBuilder("a");
tagBuilder.MergeAttribute("href", url);
tagBuilder.InnerHtml = linkText;
return MvcHtmlString.Create(tagBuilder.ToString(TagRenderMode.Normal));
}
Then you could make your template like:
<p class="post-info">
<p class="post-info" data-bind="text UserName"></p>
<span data-bind="Body"></span>
<p class="post-footer">
#Html.ActionLink("Comments (${CommentCount})", "IndividualPost", "Post",
new {id = "${id}"}, true)
</p>
</p>
the serverside html generated would then look like:
<p class="post-info">
<p class="post-info" data-bind="text UserName"></p>
<span data-bind="Body"></span>
<p class="post-footer">
Comments (${CommentCount})
</p>
</p>
which in turn is a perfect template in my opinion.
The reason for an ActionLink extension is the fact that the normal Html.ActionLink encodes your url to /Post/IndividualPost/%24%7Bid%7D which doesn't work for the template
option 1:
- your posts viewmodel is probably coming from the server, it could contain the link.
{
title:'post title',
commentsUrl:'/Indivdualpost/comments/123'
}
on the server
return new post { comment='post title', commentsUrl=Url.Action('Comments','Individualposts', new {id=1234}); }
and then render the comments url in the template:
<a data-bind="attr: {href:commentsUrl}">comments</a>
option 2:
script using a form
<form id="frm" action="#Url.Action("Comments","IndividualPost")>
<input type="hidden" name="id" id="postid"/>
<!-- template stuff -->
</form>
and in the template
<p class="post-footer">
<a data-bind="click:function(){ $('#postid').val(${$id}); $('#frm').submit(); }">comments</a>
</p>
(the click attribute is quite ugly, should be improved using a binding handler or a viewmodel function ( http://www.knockmeout.net/2011/08/simplifying-and-cleaning-up-views-in.html ))

Resources