Originally I repeated lines of code for each menu item and just hard coded the various menu item values but then I came across helpers and taught I would give it a try. Now 6 lines of code (for each menu item) are reduced to one (for each item), and I have a single place to go to alter anything instead of changing it in 5 places. All great stuff. Here is the code:
#helper MenuItem(string action, string controller)
{
<a href="#Url.Action(action, controller)" id="#controller">
<div class="MenuItem">
<img src="#("/XXX.YYY.Web/Content/Images/Icons/Menu/mnu"+controller+".png")" /><br />
//I had to put the XXX.YYY as a literal string because the ~ didn't work, it was quoted literally also instead of showing the home folder.
#controller
</div>
</a>
}
My problem is that it works when I use it inline, say at the top of my _Layout.cshtml with the following lines of code:
#MenuItem("Index", "Home")
#MenuItem("Index", "Chart")
But when I remove it out to a generic helper called LayoutHelpers.cshtml under the App_Code folder so I can reuse it, and alter the code accordingly as follows:
#LayoutHelpers.MenuItem("Index", "Home")
#LayoutHelpers.MenuItem("Index", "Chart")
Note: Nothing in the actual helper changed. Only the above 2 lines in the _Layout.cshtml file changed.
When I make those changes I get the following error:
Compilation Error
Description: An error occurred during the compilation of a resource required to service this request. Please review the following specific error details and modify your source code appropriately.
Compiler Error Message: CS0103: The name 'Url' does not exist in the current context
Source Error:
Line 3: #helper MenuItem(string action, string controller)
Line 4: {
Line 5:
Line 6:
Line 7: ##
Now the curious thing is, notice how it works on line 7 "mnuHome.png" as opposed to mnucontroller.png. Yet it says that line 5 is in error.
I also have a problem with the ~ not working in the helper. ie. the ~/Content is shown as a literal string instead of it being compiled to a proper path which should always point to the home folder of the app.
Following is a link that I am using for reference:
http://weblogs.asp.net/jgalloway/archive/2011/03/23/comparing-mvc-3-helpers-using-extension-methods-and-declarative-razor-helper.aspx
Specifically less than 1/4 of the way down the page under the heading "Razor Declarative Helpers". From here on.
Thanks in advance for your help.
The standard helpers (such as UrlHelper and HtmlHelper) are not available in a Razor inline #helper. If you need to use it will need to pass the UrlHelper as parameter to your helper:
#helper MenuItem(UrlHelper url, string action, string controller)
{
<a href="#url.Action(action, controller)" id="#controller">
<div class="MenuItem">
<img src="#url.Content("~/XXX.YYY.Web/Content/Images/Icons/Menu/mnu"+controller+".png")" />
<br />
#controller
</div>
</a>
}
and then when calling pass the correct instance:
#LayoutHelpers.MenuItem(Url, "Index", "Home")
#LayoutHelpers.MenuItem(Url, "Index", "Chart")
Related
Summary
I am wanting to correct my use/calling of the RenderPartialAsync method while trying to render an ASP.NET Core partial view.
Details
I'm trying to render a partial view in my simple ASP.NET Core 5.0 website:
<td>
#await Html.RenderPartialAsync("_Listings", item.Listings);
</td>
When I try the following, I keep getting a compiler error:
Cannot implicitly convert type 'void' to 'object'
I don't understand what it's trying to complain about. I'm guessing that this method awaits a Task which doesn't return anything ... but it's wanting something to be returned?
I thought that the RenderPartialAsync method will render the contents to the response stream inside that method... not return some HTML which I then need to do something, with.
In this context, "renders" means the method writes its output using Writer.
What am I doing wrong? Is it the placement of my razor 'code snippet' start/end code block or something?
Also, I tried using the Html.RenderPartial (notice this is the SYNC method) and I got a warning about how this blocks AND also the same error message.
Update #1
What is the return type of item.Listings
Answer: IEnumerable<Listing>
So I had to change the code from:
<td>
#await Html.RenderPartialAsync("_Listings", item.Listings);
</td>
to
<td>
#{
await Html.RenderPartialAsync("_Listings", item.Listings);
}
</td>
I don't understand why but this now works.
My guess is that #await <stuff> is TWO commands, not one .. so it needs to get handled differently. While #{ stuff } is a code block section.
But the proper answer should be to use Tag Helpers instead.
so this:
<td>
<partial name="_Listings" model="item.Listings" />
</td>
The short story is I have a list of blog articles which I'm casting to a model which contains .wordCount. This is calculated by striping the HTML from the output of the RTE and then calculating the lengh. The output of the RTE can contain Macros.
On first load, i.e from a view, the output of the RTE renders my Macros as HTML. However, when I refresh my list articles using a clientside AJAX the output of the RTE is rendered differently. My Macros now look like this: <?UMBRACO_MACRO macroAlias="ArticleAudio" audioPicker="6068" audioPosition="left" audioTitle="Interview Audio" />
I also get the error System.InvalidOperationException: 'Cannot render a macro when there is no current PublishedContentRequest.
public static Article ToArticle(this IPublishedContent item)
{
string rawText = HelperFunctions.StripHTML(item.GetPropertyValue<string>("richText"));
...
}
public static string StripHTML(string htmlString)
{
string pattern = #"<(.|\n)*?>";
return Regex.Replace(htmlString, pattern, string.Empty);
}
Expected
<div class="audio-player">
<audio style="display: none;">
<source src="https://www.address.com/media/77390/interview.mp3" type="audio/mpeg">
Your browser does not support the audio element.
</audio>
</div>
My Output
<?UMBRACO_MACRO macroAlias="ArticleAudio" audioPicker="6068" audioPosition="left" audioTitle="Interview Audio" />
Thank you in advance for your help
When you get the property value, it will have the placeholder string for the macro in it. There is some code that runs as part of rendering the page that turns those into the actual Macros. I can't remember off the top of my head where that lives though.
Can you post the full code of the controller you are calling to update via AJAX? Is it a surface controller, an API controller, or something different?
I've been working on a relatively simple application in ASP.NET Core that displays the status of various nodes in a network. It displays their latest status, and some other information.
For this, I've made a controller which has an action that takes a node's name, performs a lookup in the node manager, and returns the detail view for that specific node. This controller action is implemented like so:
public IActionResult Detail(string name)
{
// Maybe redirect this to the node detail overview once it's done?
if (string.IsNullOrEmpty(name))
return View("PageNotFound");
var viewModel =
Current.NodeManager.Statuses.FirstOrDefault(s => s.Node.Name.Equals(name, StringComparison.OrdinalIgnoreCase));
if (viewModel == null)
return View("PageNotFound");
return View(viewModel);
}
I'm linking to this action from my overview page. The HTML I'm using to do this is:
<a asp-area="" asp-controller="Node" asp-action="Detail" asp-route-id="#status.Node.Name">#status.Node.DisplayName</a>
For one of my nodes, this produces the following link:
http://example.com/Node/Detail/Temeria
And from what I understand from the ASP.NET Core documentation, the controller should capture "Temeria" here as the argument for the Detail action of NodeController, but it reliably refuses to do so. name here is always null.
I've also messed around with the routing in Startup.Configure, by adding the following route to the controller:
routes.MapRoute(
name: "Node Detail",
template: "{controller=Node}/{action=Detail}/{name}");
But unfortunately, to no avail. Every time I invoke the action, be it via clicking the link I've outlined above, or visiting the detail action manually by typing in the URL in my browser, ASP.NET Core spits out the following log line:
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker:Information: Executing action method Aegis.Controllers.NodeController.Detail (Aegis) with arguments () - ModelState is Valid
I'm at a loss here - am I missing something really obvious here, or does ASP.NET Core not work with primitive types and should I resort to the "model binding" I've been seeing in the guides (which seems to be a bit overkill for an action as simple as this one)?
I've seen various other questions similar to this one, but none of them were for ASP.NET Core. I've done similar projects in classic ASP.NET, and never had this issue with that framework.
Any help would be greatly appreciated. Thanks for your time!
The key is in this line:
<a asp-area="" asp-controller="Node" asp-action="Detail" asp-route-id="#status.Node.Name">#status.Node.DisplayName</a>
I've changed asp-route-id to asp-route-name and everything works fine. So the answer is:
<a asp-area="" asp-controller="Node" asp-action="Detail" asp-route-name="#status.Node.Name">#status.Node.DisplayName</a>
In a previous post I asked a question about getting started with helpers. I was successful but when I tried to use the technique 1. To write a different helper based on Html.RenderAction, and 2. To pass in my own custom helper I got errors once they were exported to App_Code.
Again, to emphasise, they work inline, but not when exported to App_Code.
Here is the original code:
Many parts of my code have only the following:
<section class="Box">
#{ Html.RenderAction("PageOne"); }
</section>
Many other parts have this:
#if (#Model.PageTwo)
{
<section class="Box">
#{ Html.RenderAction("PageTwo"); }
</section>
}
So my first step was to extract out into an inline helper the following which could be used in all of my code blocks above:
#helper Item(HtmlHelper html, string action, string htmlClass)
{
<section class="#htmlClass">
#{ html.RenderAction(action); }
</section>
}
The helper above allows me to replace all the code blocks that look like the first code segment above with this line:
#Item(Html, "PageOne", "Box")
Then I went on to write the second helper which looks like this:
#helper Item(HtmlHelper html, string action, string htmlClass, bool test)
{
if (test)
{
#Item(html, action, htmlClass)
}
}
This helper allows me to replace all the code blocks that look like the second code segment above with this line:
#Item(Html, "PageTwo", "Box", #Model.ShowThisTorF)
My main question once again is, this works inline, so why not when I remove it to App_Code.
Once I move it to App_Code I get the following errors:
The first problem is regarding adding a using reference (because HtmlHelper is ambiguous) to which I add the following line of code:
#using HtmlHelper=System.Web.Mvc.HtmlHelper
This removes the first error but then I get another error:
System.Web.Mvc.HtmlHelper does not contain a definition for
'RenderAction' and no extension method 'RenderAction' accepting a
first argument of type 'System.Web.Mvc.HtmlHelper' could be found (are
you missing a using directive or an assembly reference?)
I have also tried the other reference but with no result:
#using HtmlHelper=System.Web.WebPages.Html.HtmlHelper
Another problem that I am having is that I don't think the second block will work once I get the first one working. Even though it worked fine inline.
Also, I know its obvious, but if I don't say it here, someone will ask it in their answer. When I moved it out to the file App_Code, I did indeed add the file name prefix as required so the one line lumps of code looked something like:
#Helpers.Item(Html, "PageOne", "Box")
#Helpers.Item(Html, "PageTwo", "Box", #Model.ShowThisTorF)
Thanks for any help with this.
The correct HtmlHelper inside helpers in the App_Code directory is the System.Web.Mvc.HtmlHelper.
Because RenderAction is an extension method you need to add a using for the namespace where it is declared which is #using System.Web.Mvc.Html
So this should work assuming your file is named Helpers.cshtml and in the App_Code directory:
#using HtmlHelper=System.Web.Mvc.HtmlHelper
#using System.Web.Mvc.Html
#helper Item(HtmlHelper html, string action, string htmlClass)
{
<section class="#htmlClass">
#{ html.RenderAction(action); }
</section>
}
#helper Item(HtmlHelper html, string action, string htmlClass, bool test)
{
if (test)
{
#Item(html, action, htmlClass)
}
}
And the usage:
#Helpers.Item(Html, "PageOne", "Box")
#Helpers.Item(Html, "PageTwo", "Box", #Model.ShowThisTorF)
I've come to an MVC3 project I wrote just a week ago which has stopped working and is throwing the following error:
Error 10
The call is ambiguous between the following methods or properties: 'System.Web.Mvc.Html.RenderPartialExtensions.RenderPartial(System.Web.Mvc.HtmlHelper, string)' and 'System.Web.Mvc.Html.RenderPartialExtensions.RenderPartial(System.Web.Mvc.HtmlHelper, string)'
What is the reason for this? I haven't changed anything in project recently for it to bork. The code I call it in looks like this:
<div class="page-body">
#if(!String.IsNullOrWhiteSpace(ViewBag.ErrorMessage)) {
// Output error message
Html.Raw(ViewBag.ErrorMessage);
} else {
// Render upload form
Html.RenderPartial("_UploadForm");
}
</div>
You are missing # symbols front of your Html.Raw because teh method reutrns a string back hence requires the #symbol
For your knowledge taken from MSDN : The Razor syntax # operator
HTML-encodes text before rendering it to the HTTP response. This
causes the text to be displayed as regular text in the web page
instead of being interpreted as HTML markup.
Please use it this way
<div class="page-body">
#if(!String.IsNullOrWhiteSpace(ViewBag.ErrorMessage)) {
#Html.Raw(ViewBag.ErrorMessage);
} else {
// Render upload form
Html.RenderPartial("_UploadForm");
}
</div>