Umbraco - Render .Net User Control (ascx) macro with Razor - razor

I have a razor script in Umbraco that is quite complex and I want at some point of it to render a macro in it.
The macro which is called SuggestionBox is actually a user control (.ascx) and traditionally this is referenced on the template using
<umbraco:macro Alias="SuggestionBox" language="cshtml" runat="server"></umbraco:macro>
But now I need to call it from the razor script instead so I tried;
#Html.Raw(umbraco.library.RenderMacroContent("SuggestionBox", Model.Id))
as well as:
#RenderPage("SuggestionBox")
No luck so far as I'm sure I'm using these wrongly.
I read somewhere it might be infeasible if the page is wrapped in a masterpage.
It works if I add it to the Template like I traditionally would:
<umbraco:macro Alias="EventsRenderer" language="cshtml" runat="server"></umbraco:macro>
<div class="talkingPointPanel">
<h3><umbraco:Item field="talkingPoinstSuggestionText" runat="server"></umbraco:Item></h3>
<umbraco:macro Alias="SuggestionBox" language="cshtml" runat="server"></umbraco:macro>
</div>
Where EventsRenderer renders the page that should ideally contain the SuggestionBox.
using
#Html.Raw(umbraco.library.RenderMacroContent("<?UMBRACO_MACRO macroAlias=\"SuggestionBox\" />", Model.Id))
Gives me this error:
<!-- Error generating macroContent: 'System.Web.HttpException (0x80004005): HtmlForm cannot render without a reference to the Page instance. Make sure your form has been added to the control tree.
at System.Web.UI.HtmlControls.HtmlForm.Render(HtmlTextWriter output)
at System.Web.UI.Control.RenderControlInternal(HtmlTextWriter writer, ControlAdapter adapter)
at System.Web.UI.Control.RenderChildrenInternal(HtmlTextWriter writer, ICollection children)
at System.Web.UI.Control.RenderChildrenInternal(HtmlTextWriter writer, ICollection children)
at umbraco.presentation.templateControls.Macro.Render(HtmlTextWriter writer)
at System.Web.UI.Control.RenderChildrenInternal(HtmlTextWriter writer, ICollection children)
at umbraco.library.RenderMacroContent(String Text, Int32 PageId)' -->
Any ideas?

<umbraco:Macro runat="server" language="cshtml">#{
HtmlTextWriter writer = new HtmlTextWriter(this.Output);
var navigation = new umbraco.presentation.templateControls.Macro();
navigation.Alias = "Navigation";
navigation.MacroAttributes.Add("ulclass", "art-vmenu");
navigation.MacroAttributes.Add("level", 2);
navigation.RenderControl(writer); }</umbraco:Macro>
Try something like this. It works for me ... I have made a Navigation macro. Be Aware though your variables should be given in toLower, if caps are used, the parameters will not come through.

In Umbraco 4.10+ To call a macro inside Razor script, use:
#Umbraco.RenderMacro("macroNameHere", new { propertyName1 = CurrentPage.pageProperty }))

Try something like this:
#Html.Raw(umbraco.library.RenderMacroContent("<?UMBRACO_MACRO macroAlias=\"SuggestionBox\" />", Model.Id))

Related

Add components based on string variables in Blazor

We're creating a dynamic page of components in Blazor. The intention is to have dynamic applets displayed on a page. The idea is that we have a list of strings which correspond to Component names. We read through the string list and for each one, instantiate a blazor component or render fragment. These are just simple components, no passed in parameters or the like. ie:
string[] componentsStrings = {"Component1", "Component2"};
Expected output:
<Component1 />
<Component2 />
We can't come up with a way to do this. It seems like a fairly standard thing to do, but perhaps not? Does anyone know if this is even possible?
You will have to programmatically create a component which adds your custom components on the page using RenderTreeBuilder.
Chris Sainty has a blog post on this which you can read here: https://chrissainty.com/building-components-via-rendertreebuilder/
Basically there is an override for BuildRenderTree in the ComponentBase class which can be used:
public class Menu : ComponentBase
{
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
base.BuildRenderTree(builder);
builder.OpenElement(0, "nav");
builder.AddAttribute(1, "class", "menu");
}
}
Here is another tutorial.
Some tips from here:
Place base.BuildRenderTree(builder); at the start of the
BuildRenderTree method , not at the end.
Always start with the value 0 for the sequence parameter.

JavaFX WebView: link to anchor in document doesn't work using loadContent()

Note: This is about JavaFX WebView, not Android WebView (i. e. I have seen "Android Webview Anchor Link (Jump link) not working").
I display a generated HTML page inside a javafx.scene.web.WebView that contains anchors and links to those anchors like this:
<p>Jump to Introduction</p>
some text ...
<h1 id="introduction">Introduction</h1>
more text ...
I use this code to load the HTML into the WebView:
public void go(String location) {
try {
// read the content into a String ...
String html = NetUtil.readContent(new URL(location), StandardCharsets.UTF_8);
// ... and use loadContent()
webview.getEngine().loadContent(html);
} catch (IOException e) {
LOG.error(e);
}
}
Everything is rendered correctly, but if I click on the link named "Introduction", nothing happens.
The HTML however is correct, which I checked by instead using this code:
public void go(String location) {
// use load() to directly load the URL
webview.getEngine().load(location);
}
Now, everything worls fine.
The problem seems to be somehow because the document URL of the WebView is null when using loadContent(), but since it's a readonly property, I have no idea how to make it work.
I need to use loadContent(), because the HTML is generated on the fly, and if possible in any way, I don't want to have to write it out to a file just to make anchor links working. Is there a way to fix this?
EDIT
I filed a bug for JavaFX.
It's probably another WebEngine bug. A lot of that code is just a native libraries wrapped in api, so we can't modify it in runtime to fix some disabilities.
If you are able to change the structure of generated file you can implement scrolling to element in js:
<script>
function scrollTo(elementId) {
document.getElementById(elementId).scrollIntoView();
}
</script>
<a href='#' onclick=scrollTo('CX')>Jump to Chapter X</a>
<h2 id="CX">Chapter X</h2>
If you can't change the structure, there is some steps that I've made to try to fix it and some suggestions - at first I've set value of location by reflections after loadContent for sure:
Field locationField = WebEngine.class.getDeclaredField("location");
locationField.setAccessible(true);
ReadOnlyStringWrapper location = (ReadOnlyStringWrapper) locationField.get(engine);
location.set("local");
But in fact, keeping state of actual location is just an information for you and manipulating this changes nothing. I've also found a way to set url from js (just a long shot, we don't have any specific details why it's not working):
window.history.pushState("generated", "generated", '/generated');
Of course we can't because of:
SecurityError: DOM Exception 18: An attempt was made to break through the security policy of the user agent.
I think you should forget about loadContent(). You said that you didn't want to write generated content to file. A little dirty hack but really helpful for you could be wrapped http server on random and unused port in your application. You don't even need external libraries because Java has simple utilities like that:
HttpServer server = HttpServer.create(new InetSocketAddress(25000), 0);
server.createContext("/generated", httpExchange -> {
String content = getContent();
httpExchange.sendResponseHeaders(200, content.length());
OutputStream os = httpExchange.getResponseBody();
os.write(content.getBytes());
os.close();
});
server.setExecutor(null);
server.start();
You can also use another browser to display your page, e.g. JCEF (Java Chromium Embedded Framework).

Backbone toJSON not rendering

when I use Backbone toJSON method of the model like this:
this.$el.html(this.model.toJSON());
It doesn't render model into view root element ( more than one attribute ).
But when I get one property from the model, like this;
this.$el.html(this.model.get("city"));
It is rendered properly.
Also, when I use template in first case (toJSON) - it is rendered fine.
this.$el.html(this.template(this.model.toJSON());
Why is that ?
Thanks
this.$el.html(this.model.toJSON());
You're using the html method of jQuery, which expects a string (or a DOM element, or a jQuery element), to display a JSON object.
this.$el.html(this.template(this.model.toJSON());
Here you're using a template method which, I assume, is taking a JSON object to evaluate a template that will return you a string. The htmlmethod receives this string and displays it.
this.$el.html(JSON.stringify(this.model.toJSON()));
This would display the result of this.model.toJSON() (but won't do the same as using your template method).
So, basically this.template will be (in most of the cases) a compiled version of the html template which you have for the view.
It will have placeholders in it, and will take parameters with the same key as placeholders in the template. For example (Handlebars templates),
<section id="{{id}}">
<header>{{header_text}}</header>
</section>
Considering the above code as a template, when you compile and store it in this.template, it returns a function, which takes a json object as a parameter, so now this.template is a function.
You can call it like below,
var html_text = this.template({
id : "main_content",
header_text : "Hi Welcome !!"
});
this.$el.html(html_text);
After the execution, el's contents will be
<section id="main_content">
<header>Hi Welcome !!</header>
</section>
So when you do this.$el.html(this.template(this.model.toJSON());, it actually generates the required json parameter for the this.template method for you, hence works fine.
And as Loamhoof said, in this.$el.html(this.model.get("city")); you use the html method which will set the html content of the el based on the property value of the model.

Orchard CMS: Custom Taxonomy View Template not working

I'm using a taxonomy part in one of my custom content types, and using the shape tracer, I was able to create a custom view template for that control (Fields.Contrib.TaxonomyField.cshtml).
When I shape trace the element on the page, under template it indeed shows my custom template exactly as I have it on the external file:
#using Orchard.Utility.Extensions;
#using Orchard.ContentManagement;
#{
var terms = (IEnumerable<Contrib.Taxonomies.Models.TermPart>)Model.Terms;
string name = #Model.ContentField.Name;
}
#if (Model.Terms.Count > 0) {
#(new HtmlString( string.Join(", ", terms.Select(t => Html.ItemDisplayLink(Html.Encode(t.Name), t.ContentItem ).ToString()).ToArray()) ))
}
<div>TEST TEXT</div>
However, when I tab over to the actual outputted HTML, it is showing the standard, built in template, and isn't using my customizations.
<p class="taxonomy-field">
Coffee Shop
</p>
According to the shape tracer, it is using my custom template:
Shape Fields_Contrib_TaxonomyField
Active Template
~/Themes/Continuum/Views/Fields.Contrib.TaxonomyField.cshtml
Display Type Detail
this feels like a bug... everything looks like it's wired up correctly... is there something else I need to do to use my custom view template for taxonomy?
Many thanks!
I figured out the problem. Turns out there was another template created (likely by accident) which was url-specific:
Fields.Contrib.TaxonomyField-url-venues.cshtml
that's the url I was on, and this had the default template still in it. After deleting it, it finally used my custom template.
User error, sorry!!

Pass HTML from controller to view

I've made a structure to retrieve from database, based on the role given, to return menu items.
Now, I need to make a recursive method to render this HTML in the controller and pass this HTML to view. But i just don't know how to write native HTML in the controller.
Any suggestions?
The controller should not handle any of the HTML at all. That's what the view in MVC is for. Your controller should pass a data structure from the model, and the view should render that data structure as HTML.
Easier than you might think:
Controller:
ViewData["html"] = #"<a href='http://www.stackoverflow.com'>Stack Overflow</a>";
View:
<%= ViewData["html"] %>
I do agree this isn't the best method. I would suggest you write the html markup in your view and substitute the values from your model instead.
e.g.
Controller:
ViewData["Title"] = "Stack Overflow";
ViewData["Url"] = #"http://www.stackoverflow.com";
View:
<a href="<%=Html.Encode( ViewData["Url"] )%>">
<%=Html.Encode( ViewData["Title"] )%></a>
If you have to create many, you could use a partial view / user control to encapsulate the common markup.
It's definitely not the idea of MVC (or whatever you're doing) to render HTML in the Controller. HTML has to be handled in the view. What if you want to provide an alternative UI (e.g. Windows Application)? HTML does not really fit into an WinApp.
You can use an HTML helper to create the HTML. For example, we have a Menu system that is very complex, so we create an HtmlHelper extension. Here's a simple html extension (I use TagBuilders to make the HTML easier ... very handy when you have lots of nested tags).
public static String MyNewExtension(this HtmlHelper html, String extraVariable)
{
if (html == null)
{
throw new ArgumentNullException("html");
}
TagBuilder h1Tag = newTagBuilder("h1");
h1Tag.InnerHtml = extraVariable;
return h1Tag.ToString();
}