How do I define a method in Razor? - c#

How do I define a method in Razor?

Leaving alone any debates over when (if ever) it should be done, #functions is how you do it.
#functions {
// Add code here.
}

You mean inline helper?
#helper SayHello(string name)
{
<div>Hello #name</div>
}
#SayHello("John")

It's very simple to define a function inside razor.
#functions {
public static HtmlString OrderedList(IEnumerable<string> items)
{ }
}
So you can call a the function anywhere. Like
#Functions.OrderedList(new[] { "Blue", "Red", "Green" })
However, this same work can be done through helper too. As an example
#helper OrderedList(IEnumerable<string> items){
<ol>
#foreach(var item in items){
<li>#item</li>
}
</ol>
}
So what is the difference?? According to this previous post both #helpers and #functions do share one thing in common - they make code reuse a possibility within Web Pages. They also share another thing in common - they look the same at first glance, which is what might cause a bit of confusion about their roles. However, they are not the same. In essence, a helper is a reusable snippet of Razor sytnax exposed as a method, and is intended for rendering HTML to the browser, whereas a function is static utility method that can be called from anywhere within your Web Pages application. The return type for a helper is always HelperResult, whereas the return type for a function is whatever you want it to be.

You could also do it with a Func like this
#{
var getStyle = new Func<int, int, string>((width, margin) => string.Format("width: {0}px; margin: {1}px;", width, margin));
}
<div style="#getStyle(50, 2)"></div>

Razor is just a templating engine.
You should create a regular class.
If you want to make a method inside of a Razor page, put them in an #functions block.

You can also just use the #{ } block to create functions:
#{
async Task<string> MyAsyncString(string input)
{
return Task.FromResult(input);
}
}
Then later in your razor page:
<div>#(await MyAsyncString("weee").ConfigureAwait(false))</div>

Here is how the list helper is written in ASP.NET Core 3
You can now include HTML markup in the body of a method declared in a code block as a local method as previously, or in an #functions block. The method should return
void, or Task if it requires asynchronous processing :
#{
void Template(string[] listItems, string style)
{
<ul>
#foreach (var listItem in listItems)
{
<li class="#style">#listItem</li>
}
</ul>
}
}

MyModelVm.cs
public class MyModelVm
{
public HttpStatusCode StatusCode { get; set; }
}
Index.cshtml
#model MyNamespace.MyModelVm
#functions
{
string GetErrorMessage()
{
var isNotFound = Model.StatusCode == HttpStatusCode.NotFound;
string errorMessage;
if (isNotFound)
{
errorMessage = Resources.NotFoundMessage;
}
else
{
errorMessage = Resources.GeneralErrorMessage
}
return errorMessage;
}
}
<div>
#GetErrorMessage()
</div>
You could also use the code block below. It is much cleaner and has more functionality. You can also insert variables above and functions below. Instead of using 2 seperate code blocks.
#{
string exampleVariable = "just an example variable";
string anotherExampleVariable = "just another example variable";
string GetErrorMessage()
{
var isNotFound = Model.StatusCode == HttpStatusCode.NotFound;
string errorMessage;
if (isNotFound)
{
errorMessage = Resources.NotFoundMessage;
}
else
{
errorMessage = Resources.GeneralErrorMessage
}
return errorMessage;
}
}

Related

Bind a List of object using Razor page

I have razor pages .
I have prepared in a handler a list of(visitorTypes)
I only want to bind them . there is something that i have missed , but i dont know what
Here is my c# code
[ModelBinder(Name ="Visitors")]
public ICollection<VisitorType> VisitorTypes { get; set; }
public IActionResult OnGetListOfVisitorTypeAsync()
{
VisitorTypes = _db.VisitorTypes.ToList();
return RedirectToPagePermanent("/Visitors",VisitorTypes);
}
And here is my razor page
<div class="container">
<form method="get" asp-page-handler="ListOfVisitorType" >
#foreach (var item in Model.VisitorTypes)
{
<label>#item.VisitorTypeName.ToString() </label>
}
</form>
</div>
can someone please explain what im doing wrong
(I have tried to return the list , i have tried to make it a void method , but none of them works with me )
Here Is The modal
private string _VisitorTypeName { get; set; }
public string VisitorTypeName { get {return _VisitorTypeName; } set { _VisitorTypeName = value; } }
ICollection<Visitor> Visitors { get; set; }
What if you generate your data when your View get loaded, something like this:
public IActionResult Visitors()
{
VisitorTypes = _db.VisitorTypes.ToList();
return View(VisitorTypes);
}
You may have passed wrong model with "#model ?" in razor page.
This is pretty Simple
First you need to know how actually MVC works and what it's:
MVC is a architectural pattern , we keep all things loosely coupled in terms of MODEL, View and Controller
So it's doesn't means that cannot write code directly in "Razor View",
For example how we write JavaScript directly in HTML or View. for decoupling purpose we create .JS file to separate script code from View(HTML).
Now comes to actual work
First before accessing List(VisitorTypes) it must be declare and initialize with value in the razorview itself
#{
List<VisitorTypes> listVisitorTypes = _db.VisitorTypes.ToList();
}
And finally render the list:
#foreach (var item in listVisitorTypes )
{
#item.VisitorTypeName.ToString()
}

return custom html from ViewComponent

In an ASP.NET Core app I want to return custom html from a ViewComponent. I can return custom text, but the html will be encoded instead of being embedded:
public class BannerViewComponent : ViewComponent
{
public async Task<IViewComponentResult> InvokeAsync(string param1, int param2)
{
return Content("<strong>some custom html</strong>");
}
}
I use it in my .cshtml page:
#await Component.InvokeAsync("BannerView")
On the page this will Show as <strong>some custom html</strong> instead of some custom html.
How do I directly return HTML instead of text from the ViewComponent?
If you don't want to return a view you can return HTML this way without a view:
return new HtmlContentViewComponentResult(new HtmlString("Not bold - <b>bold</b>"));
Your ViewComponent could have its own view as well and you can render the html there. The solution would be the following:
public class BannerViewComponent : ViewComponent
{
public async Task<IViewComponentResult> InvokeAsync(string param1, int param2)
{
string model = "<strong>some custom html</strong>";
return View("Index", model);
}
}
Add the following to your Views folder: Views\Shared\Components\BannerViewComponent\Index.cshtml and put the following in the ViewComponent's view:
#model string
#Html.Raw(Model)
You can change the model to be a class instead of just a string so that you can structure the ViewComponent's output, but the key part is the Html.Raw() method to output unencoded html.
Although I would recommend using a view in most cases (and putting all the HTML in the view rather than using it just to output the HTML created by the view component), for very simple components you may want to consider this:
The Invoke() method on the view component does not need to return IViewComponentResult, it can return HtmlString.
For example:
public HtmlString Invoke()
{
return new HtmlString(#"<b>Hello World</b>");
}

should I be using an #html.renderpartial or #html.renderaction

I'm trying to bring in my menu.
In my _Layout.cshtml page I have
<div class="wrapper">
<!-- Navigation -->
#Html.RenderAction("Navigation", "Nav")
The Nav Controller looks like this
public ActionResult Navigation()
{
var pages = pageRepository.Pages;
return View(pages);
}
The Navigation View Looks like this
#model IEnumerable<Site.Domain.Entities.Page>
#{
Layout = null;
List<Site.Domain.Entities.Page> pages = new List<Site.Domain.Entities.Page>();
foreach(var page in Model)
{
pages.Add(page);
}
}
#foreach (var link in Model)
{
if (link.ParentPage == "Home")
{
<li>#link.PageTitle</li>
<ul>
#foreach (var subLink in pages)
{
if (subLink.ParentPage == link.PageTitle)
{
<li>#subLink.PageTitle</li>
}
}
</ul>
}
}
The view works fine when I go to .../nav/navigation
What I'm trying to do is bring this into my _Layout page so that I can use it as my menu.
I continue to get an error with #Html.RenderAction("Navigation", "Nav")
The error says "The best overloaded method match for 'System.Web.WebPages.WebPageExecutingBase.Write(System.Web.WebPages.HelperResult)' has some invalid arguments"
Should I be using this as a partial? What is the best way to go about this?
Thanks for any advice!
For what you're trying to do, #Html.RenderAction(..) is the correct call. RenderAction is ChildActionExtension and will need to add that attribute to the controller.
Your controller should look something like below. Note that you will want to return a PartialView as well.
[ChildActionOnly]
public ActionResult Navigation()
{
var pages = pageRepository.Pages;
return PartialView(pages);
}
The Render action does not return HTML, but rather adds the content to the response. With that, your view should look like:
#{#Html.RenderAction("Navigation", "Nav");}
Reference: http://msdn.microsoft.com/en-us/library/ee721274(v=vs.108).aspx
Because Html.RenderAction is a void and does not return a value, you need to "escape" the call with braces
#{Html.RenderAction("Navigation", "Nav");}
In your controller, you should return a partial view instead.
public ActionResult Navigation()
{
var pages = pageRepository.Pages;
return PartialView(pages);
}

Integer extension in mvc razor view

I am all new with ASP.NET MVC and Extension methods.
I have created two Extensions that i want to use in my View:
public static class Extensions
{
public static string ToYesNo(this bool value)
{
return value ? "Yes" : "No";
}
public static string MonthToString(this int value)
{
return (value >= 1 && value <= 12) ? CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(value) : "";
}
}
I can use ToYesNo with a bool in the View, but i cannot view MonthToString with an integer. I get:
'int' does not contain a definition for 'MonthToString'
The Extensions are in a namespace called BitvaerkAdmin.Models, and i reference that in th cshtml file.
Why can't i use my integer extension?
Edit:
I reference the extensions in my view like this:
#using BitvaerkAdmin.Models
<h3>
#ViewBag.Month.MonthToString()
</h3>
#foreach (Order order in ViewBag.Orders)
{
<td>
#order.Valid.ToYesNo()
</td>
}
OK, now that you have shown your code it is clear why it doesn't work. You use ViewBag (the root of all evil in ASP.NET MVC and the origin of all problems that people are having - little addition from the author of this answer).
Once you borrow its path the fall to the abyss is eminent. This fall will be accelerated by the cast that you need to perform in order to make it work:
#((int)(ViewBag.Month).MonthToString())
Simply try running the following console application and you will understand that dynamic variables cannot be used to dispatch extension methods:
public static class Extensions
{
public static string MonthToString(this int value)
{
return (value >= 1 && value <= 12) ? CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(value) : "";
}
}
class Program
{
static void Main()
{
dynamic foo = 123;
Console.WriteLine(foo.MonthToString()); // crash at runtime
}
}
See why I always critique ViewBag when I see people using it? Because it leads you to all kind of strange things. You lose Intellisense, you cannot dispatch extension methods on dynamic variables, ...
So actually you don't need to cast. You shouldn't use any ViewBag/ViewData at all. You should be using strongly typed view models:
#using BitvaerkAdmin.Models
#model MyViewModel
<h3>
#Model.Month.MonthToString()
</h3>
#foreach (Order order in Model.Orders)
{
<td>
#order.Valid.ToYesNo()
</td>
}
and to avoid the foreach loop you could use display templates:
#using BitvaerkAdmin.Models
#model MyViewModel
<h3>
#Model.Month.MonthToString()
</h3>
#Html.DisplayFor(x => x.Orders)
and then define a display template for the order which will automatically be rendered by the framework for all elements of the collection (~/Views/Shared/DisplayTemplates/Order.cshtml):
#using BitvaerkAdmin.Models
#model Order
<td>
#Model.Valid.ToYesNo()
</td>
Everything is now strongly typed and working.
After giving reference of Extention class in My view I tried as below and it worked for me.
#using NameSpace Of your Extentions class;
#{
int i = 10;
}
<span>#i.MonthToString()</span>

Calling a method in the controller

I'm a newbie about ASP.NET MVC 3, but I have a simple question.
Is it possible to call a Controller method from an CSHTML (Razor) page?
Example:
xxxControl.cs:
public String Bla(TestModel pModel)
{
return ...
}
index.cshtml:
#Bla(Model) <-- Error
Thanks.
Update:
Thanks #Nathan. This isn't a good idea to do this on this way.
The goal is: I need some formatting string for a field of the Model. But where I put the code that return a formatting String in the case?
It is considered bad practice for a view to call methods located on a controller. Usually it is a controller action which populates a model and passes this model to the view. If you needed some formatting on this model you could write an HTML helper.
public static class HtmlExtensions
{
public static IHtmlString Bla(this HtmlHelper<TestModel> htmlHelper)
{
TestModel model = htmlHelper.ViewData.Model;
var value = string.Format("bla bla {0}", model.SomeProperty);
return MvcHtmlString.Create(value);
}
}
and in your view:
#Html.Bla()
That would make unit-testing your mvc site very difficult.
Are you needing a partial view maybe? (what are you actually trying to do?)
Yes it's possible.
#using Nop.Web.Controllers;
#
var _CatalogController = EngineContext.Current.Resolve<CatalogController>();
var _model = new ProductModel();
_model = _CatalogController.PrepareProductOverviewModel(p, true, true);
}
Set the method to public if it is private.
Even the services you can call in the same manner.
var _productService = EngineContext.Current.Resolve<IProductService>();
if (Model.SubCategories.Count > 0)
{
foreach (var SubCategories in Model.SubCategories)
{
int subcategoryid = SubCategories.Id;<br>
IPagedList<Product> _products = _productService.SearchProducts(subcategoryid,0, null, null, null, 0, string.Empty, false, 0,null,ProductSortingEnum.Position, 0, 4);
}
i++
}
Simply do it like this:
xxxControl.cs action method:
public ActionResult YourView(TestModel pModel) {
//pMomdel code here
ViewBag.BlaResult = Bla(pModel);
return View(pModel);
}
index.cshtml:
#ViewBag.BlaResult

Categories

Resources