Set 'cshtml' data and return a string - c#

I have a confirmation email template .cshtml as following:
<html>
<head>
<title>company name: Email Verification - Action Required</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
Thanks for signing up for company name!
<br/>
<br/>
Thank you for signing up to company name. Please press the link below to confirm your email address <br/>
If you did not sign up for company name, please ignore this email, and accept our apologies for the inconvenience.
<br/>
<br/>
#Model.ConfirmLink
<br/>
<br/>
<br/>
Thanks,<br/>
The <a href="http://company name.com" >company name</a> Team
</table>
</body>
</html>
Is there a mechanism I can set data (#Model.ConfirmLink) and get back a result as string? (not manually replace string)

You can use RazorEngine, which uses the same syntax as Razor does, but gives you the ability to render the text without the need of ASP.NET MVC.
How your code would work:
string template = yourHtmlCodeAsString;
string result = Razor.Parse(template, new { ConfirmLink = "http://.../" });
You can load that template string from a file, a resource, etc.

Yes, you can use Razor as template engine for emails.
I use this class.
public static class EmailFactory
{
public static string ParseTemplate<T>(T model, string templatesPath, EmailType emailType)
{
string templatePath = Path.Combine(templatesPath, string.Format("{0}.cshtml", emailType));
string content = templatePath.ReadTemplateContent();
return Razor.Parse(content, model);
}
}
templatesPath - is the path where my cshtml views for email are.
EmailType is and enum that has elements that are the same as the view namse from the templatesPath directory.
The essence is Razor.Parse(content, model); it takes a string that is a valid razor view and a model and merges them together. Same as the views in ASP MVC.

Related

Why model is null after postback in ASP.NET MVC

I'm developing an ASP.NET MVC app using the latest .NET Framework and VS 2019.
In my page, I have an simple textbox and a search button. When I click the search button and postback the form, in controller, I get model.Code is NULL
Here is my code - my view model:
public class UserManagementViewModel
{
public string Code { get; set; }
// some other properties including simple and complex types
}
In View:
<td style="min-width: 300px">
#Html.TextBoxFor(m => m.Code)
</td>
In Controller:
[HttpPost]
public ActionResult UpdateUserPermissions(UserManagementViewModel model)
{
// model.Code is null here!!
return RedirectToAction("UserManagement");
}
After hours of research, I tried following:
I looked into html source and found
<input id="Code" name="Code">
Using Chrome dev tool, I change the name of the input to "Model.Code"
<input id="Code" name="Model.Code">
And this time, I got the entered value in textbox after postback.
I don't know why this happens and why the element name is not generated correctly!
What can I do?
Edit:
I have some other controls like checkbox and radio-buttons which I use pure HTMl code and name them beginning with "Model." like below instead of using #HTML helper.
And it works fine and I get the value in Action.
Workaround:
I found a workaround here: ASP.NET MVC 4 override emitted html name and id so I change my code to:
#Html.TextBoxFor(m => m.Code, new { #Name = "Model.Code" })
However this code helps me to fix my problem, I still don't know why the name generated by Razor engine is not working.
The helper method #Html.TextBoxFor works absolutely correctly. Basically, you need to use this method for almost all situations (except some situations when you need to use no-standard behavior).
Regarding your case, I think you have trouble with HtmlFieldPrefix.
This property is used for the generation of HTML input's name. We can review MVC source codes in GitHub...
MVC generated HTML input (GitHub link) using this code snippet:
private static MvcHtmlString InputHelper(HtmlHelper htmlHelper, InputType inputType, ModelMetadata metadata, string name, object value, bool useViewData, bool isChecked, bool setId, bool isExplicitValue, string format, IDictionary<string, object> htmlAttributes)
{
string fullName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name);
if (String.IsNullOrEmpty(fullName))
{
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "name");
}
TagBuilder tagBuilder = new TagBuilder("input");
tagBuilder.MergeAttributes(htmlAttributes);
tagBuilder.MergeAttribute("type", HtmlHelper.GetInputTypeString(inputType));
tagBuilder.MergeAttribute("name", fullName, true);
string valueParameter = htmlHelper.FormatValue(value, format);
bool usedModelState = false;
...
return tagBuilder.ToMvcHtmlString(TagRenderMode.SelfClosing);
}
As we can see name property calculated by method GetFullHtmlFieldName.
This method we can also inspect on GitHub (link):
public string GetFullHtmlFieldName(string partialFieldName)
{
if (partialFieldName != null && partialFieldName.StartsWith("[", StringComparison.Ordinal))
{
// See Codeplex #544 - the partialFieldName might represent an indexer access, in which case combining
// with a 'dot' would be invalid.
return HtmlFieldPrefix + partialFieldName;
}
else
{
// This uses "combine and trim" because either or both of these values might be empty
return (HtmlFieldPrefix + "." + (partialFieldName ?? String.Empty)).Trim('.');
}
}
This method calculates the name using the HtmlFieldPrefix property. So if you change this property you can modify your generated name.
I think you use partial Views and you need to render property with some prefix for correct binding.
Please review next links with examples and possible solutions:
ASP.NET MVC Partial Views with Partial Models
ASP.NET MVC3 add a HtmlFieldPrefix when calling Controller.PartialView
Define a custom HTML prefix for your .NET MVC models
in other to sumbmit model back you have to use form tags. MVC sometimes counts name as model property, but it is better to do it explicitly. This code was tested and both variants of textbox are working properly. And if you use the form you don't need any name at all, other contrary in the most cases you will get null if you use the name and form together.
#model UserManagementViewModel
<form asp-action="UpdateUserPermissions" method="post">
#Html.TextBoxFor(m => m.Code)
// or
<div class="form-group">
<label asp-for="Code" class="control-label"></label>
<input asp-for="Code" class="form-control" />
</div>
<div>
<button type="submit"> submit </button>
</div>
</form>

Build Menu Items From Controller

I am creating HTML Menus from controller. Menus are stored in database and I make html tag as below :
foreach (UIMenuModel item in list)
{
if (item.Controller != "Home")
{
string line = string.Format(#"<li><a asp-area=""{0}"" asp-controller=""{1}"" id=""{1}""
asp-action = ""{2}"" style = ""font-size:16px;;"" > {3} </a></li>", item.Area, item.Controller, item.Action, item.LinkText);
sb.Append(line);
}
}
which gives me below HTML :
<li><a asp-area="" asp-controller="CrossApproval" id="CrossApproval" asp-action="Index" style="font-size:16px;;"> Cross Approval </a></li>
Other Menu Item, Which is written in HTML itself, gives below HTML in browser.
<li><a id="CrossRequest" style="font-size:16px" href="/CrossRequest">Cross Request</a></li>
On UI, it looks perfect. However, I am not able to click and navigate to desired controller and action methods. Can someone please help me to identify while this anchor tag is not allowing me to navigate.
Use RazorLightEngine to convert plain string as rendered Razor string:
string content = "Hello #Model.Name. Welcome to #Model.Title repository";
var model = new
{
Name = "John Doe",
Title = "RazorLight"
};
var engine = new RazorLightEngine();
string result = engine.ParseString(content, model);
And then add it any place in razor view like encoded string
<div>
#Html.Raw(result)
</div>
As posted in Question, HTML with href was working fine. So, I decided to mimic the same behaviour from the controller and changed my code as below :
string line = string.Format(#"<li><a asp-area=""{0}"" id=""{1}"" href=""/{1}/{2}""
style=""font-size:16px"">{3}</a></li>", item.Area, item.Controller, item.Action, item.LinkText);
This generated a link which I can click and navigate.

How to return a link from an MVC controller with return type as "Content"

I want to return a Content type but the content has one URL in it. Below is my code.
public ActionResult Safari() {
return Content("<html>Click this URL <a href ="https://www.google.com/chrome"</html>");
}
I want the link Google.com to be rendered as hyperlink and not text. And for some reason I don't want to return a view.cshtml page.
Attempts Made:
return Content("<html>Click this URL <a href ="https://www.google.com/chrome"</html>");
return Content(#"<html>Click this URL <a href ="https://www.google.com/chrome"</html>");
return Content("<html>Click this URL <a href =/"https://www.google.com/chrome/"</html>");
Other attempts:
public ActionResult Safari() {
return Content("< a href = 'google.com/chrome' > Click this URL </ a > ");
}
If you just want a simple HTML page, then you must provide a valid page HTML with the <body> tag. You can return the link alone without the <html> tag like this:
return Content("<a href ='https://www.google.com/chrome'>Click this URL</a>");
and hope that the browser will add the missing <html>, <head>, and <body> tags to complete your page. All major browsers will do that.
However, it is better to provide a complete HTML code and not worry about the browsers:
return Content("<html><head></head><body><a href ='https://www.google.com/chrome'>Click this URL</a></body></html>");

How to add content to sections programmatically in MVC / ASP.NET?

In each of my views, I am constantly adding a style and script associated with the view. It is convention in the application we introduced, but we want to automatically apply this convention instead.
Currently our views look like this:
<p>This is the view!</p>
#section styles {
<link rel="stylesheet" href="~/Views/Home/Index.css" />
}
#section scripts {
<script src="~/Views/Home/Index.js"></script>
}
Is there a way to automate adding the styles and scripts? Perhaps in the base class of the controller, such as:
public class BaseController : Controller
{
string controller = RouteData.Values["controller"].ToString();
string action = RouteData.Values["action"].ToString();
string script = string.Format("<script src=\"~/Views/{0}/{1}.js\"></script>", controller, action);
string style = string.Format("<link rel=\"stylesheet\" href=\"~/Views/{0}/{1}.css\" />", controller, action);
// Todo: How to add script and style to sections programmatically???
}
You can use the controller/action name values in your views, and specifically, in your layout. Just edit your layout and in the head add:
<link rel="stylesheet" href="~/Views/#(ViewContext.RouteData.Values["controller"])/#(ViewContext.RouteData.Values["action"]).css" />
And before your closing body tag, add:
<script src="~/Views/#(ViewContext.RouteData.Values["controller"])/#(ViewContext.RouteData.Values["action"]).js"></script>

How I can generate the pdf using AddImageUrl from ABCpdf after the page is loaded complete

I want to add the thumbnail of a web page to pdf document,
I added like this:
Doc generatedoc = new Doc();
generatedoc.HtmlOptions.UseScript = true;
string urlToHtmlPage = "http://example.com?param1=30&param2=true";
int generatedId = generatedoc.AddImageUrl(urlToHtmlPage);
generatedoc.Save(HttpContext.Current.Server.MapPath("htmlimport.pdf"));
generatedoc.Clear();
In url I send two parameters, depending on a parameter in page is added a class for various html tags, for example:
<div class="cssClass <%=param2==true?"secondCssClass":""%>">some html tags</div>
or
<span class="<%= param2==true?"secondCssClass":"" %>"> some text </span>
Inside the style
<style type="text/css">
.secondCssClass
{
display: none;
}
</style>
pdf is generated ok but secondCssClass is not added to my tags and css is not applying
I try to set big time out but also css is not applying
I set time out like this:
generatedoc.HtmlOptions.Timeout = 1000000;
or
generatedoc.HtmlOptions.OnLoadScript = "(function(){ window.ABCpdf_go = false;
setTimeout(function(){ window.ABCpdf_go = true; }, 1000000); })();";
or RenderWait() and RenderComplete()
generatedoc.HtmlOptions.OnLoadScript = #"
window.external.ABCpdf_RenderWait();
window.external.ABCpdf_RenderComplete(true);";
but anyway CSS did not apply
when I load url in browser css is applying
some proposals?
Based on your response to the comment that you see the issue even in the browser tells that this is not an ABCPDF issue.
I think the problem lies in the following:
<div class="cssClass <%=param2==true?"secondCssClass":""%>">some html tags</div>
AND
<span class="<%= param2==true?"secondCssClass":"" %>"> some text </span>
If that code is exactly what you have in your running code, then you will need to make an adjustment those lines:
Accessing URL parameters, you need to use the Request object
URL parameters are text, and your comparison has them as boolean
Code should be as follows:
<div class="cssClass <%= Request.QueryString("param2")=="true"?"secondCssClass":"" %>">some html tags</div>
AND
<span class="<%= Request.QueryString("param2")=="true"?"secondCssClass":"" %>"> some text </span>
I recommend that you test this in the browser prior to testing it via ABCPDF.

Categories

Resources