Blazor component is rendered but no lifecycle methods are called, no other methods can be invoked - razor

I have a pretty standard Razor pages project, and I've recently introduced Blazor components into the project. I've added the _Host.cshtml, App.razor, and the other requirements to get Blazor working. The Blazor components work fine when I'm at an endpoint that is exclusively Blazor components. However, I want to embed Blazor components within my current .cshtml pages using something like
#(await Html.RenderComponentAsync<EditorComponent>(RenderMode.Server, new { Id = Model.Id}))
withing my cshtml page. This syntax renders the component fine, but it doesn't run any of the Blazor lifecycle methods(OnAfterRenderAsync) or even something like a button calling back to a Task, in my EditorComponent component.
I've included the
<script src="~/_framework/blazor.server.js"></script>
at the end of element on my layout page. Am I missing something to get this working?

I think you might need to use the component tag helper instead.
Docs
<component type="typeof(EditorComponent)" render-mode="Server" param-Id=#Model.Id/>

As you've not provided a lot of code, I've built this code to try and reproduce your problem.
I can't: it works as I would expect it to. So unless either answer above solves your problem, you'll need to provide more code.
Start point Blazor Server template.
New razor page - _Test.cshtml
#page "/Test"
#namespace BlazorApp8.Pages
#addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
#{
Layout = "_Layout";
}
<h1>Counter in a Page</h1>
<div class="m-2 p-2">
#(await Html.RenderComponentAsync<Counter>(RenderMode.Server, new { Starter = 6}))
</div>
Update Counter.razor to take a parameter.
[Parameter] public int Starter { get; set; } = 0;
protected override Task OnInitializedAsync()
{
currentCount = this.Starter;
return base.OnInitializedAsync();
}
Run and navigate to /Test.
I get the Counter page, OnInitialized sets the initial count to 6 and clicking on the button increments the counter.

Replace the blazor section in your _Layout.cshtml with =>
<head>
...
<base href="~/"/>
</head>
...
<script src="_framework/blazor.server.js" autostart="false" ></script>
<script>
Blazor.start({
configureSignalR: function(builder) {
builder.withUrl("_blazor");
}
});
</script>
...
Make sure to check your web console output. Blazor is likely throwing an error. This is pulled from every layout of every area in every app that I make. I've enjoyed a templated micro ui experience. Dotnet really makes it a breeze to proliferate your own designs via templates.

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.

Net core 2.0 - Razor Pages partial view with business itself (not MVC)

I have a sidebar view as a partial view. I want to insert it into main page.
Sidebar read some images url from database and show it into html page.
In main page, I used this code to insert sidebar:
#await Html.PartialAsync("Shared/_Sidebar")
Side bar cshtml page:
#model MyWeb.Pages.Shared._SidebarModel
...
But it raise error:
The model item passed into the ViewDataDictionary is of type 'MyWeb.Pages.IndexModel', but this ViewDataDictionary instance requires a model item of type 'MyWeb.Pages.Shared._SidebarModel'
I have research this issue, and get the answer is, must pass model from parent page, like this:
#await Html.PartialAsync("Shared/_Sidebar", Model.Urls)
If so, the parent page must handle the business of the child page. So, if I do not insert the sidebar into the main page, but to another page, I have to remove the code on the main page, and add the business code to the new page, and so on... Sidebar can not handle its own business.
In MVC, we can use RenderAction to insert a partial view with its action to handler business logic. But I have not found a way to do this in RazorPages.
Anyone help me!
There are a couple of ways including passing it as a ViewData. I found injection to be one of the cleanest ways:
Initialize your _SidebarModel as a service. This can be a scoped if you plan on using any scoped service with it (like DBContext). You can use this in your startup class:
services.AddSingleton<_SidebarModel>();
Or if you would like to initialize it here:
services.AddSingleton<>(new _SidebarModel { });
Now, instead of #model MyWeb.Pages.Shared._SidebarModel, you can use:
#inject MyWeb.Pages.Shared._SidebarModel MyModel
You can always inject and control the model from your view models as well.
You can add some basic functionality using of page view models with a class like this:
public class InjectablePage : PageModel
{
private readonly HttpContext httpContext;
public InjectablePage(HttpContext httpContext)
{
this.httpContext = httpContext;
if (httpContext.Request.Method == "GET") OnGet();
}
public virtual void OnGet() { }
}
You can extend this instead of PageModel in _SidebarModel

Umbraco - Render .Net User Control (ascx) macro with 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))

How to capture a click event on a link inside a HTML widget in GWT?

I´m evaluating GWT as one of the alternatives to develop AJAX applications for my future projects. Untill now it is as good as it gets, but now I´m stuck looking for a way to capture a click on a tag inside HTML widget. I want to write links inside the HTML but I want to process the clicks in my application, withou reloading the page. Imagine I have the following HTML:
<p>GWT is a great tool and I think it will be my preferred tool to develop web applications. To check out my samples <a id='mylink'>click here</a></p>
I want to capture the click over the "click here" part of the text. What I´ve done so far is to try to attach the id "mylink" to some sort of clickable widget and process the click with a ClickHandler for that widget, but nothing is working.
Is there a way to do that? By the way, I know very little about Javascript.
Thank you in advance.
You can also do it like this:
Anchor.wrap(DOM.getElementById("mylink")).addClickHandler(yourClickHandler);
DOM class is com.google.gwt.user.client.DOM.
Edit after comments.
OK, the method works for elements out of GWT widgets (element comes with HTML file). If you need to generate it in GWT code then you can add link element separately. But it won't work if your content goes for instance from DB.
HTMLPanel html = new HTMLPanel("GWT is a great tool and I think it will be my preferred tool to develop web applications. To check out my samples ");`
Anchor a = new Anchor("click here");
a.addClickHandler(yourClickHandler);
html.add(a);
If it is fully dynamic I don't have an idea at this point. I was trying with HTML() widget, where you can plug your click handler, but I couldn't find a right way to determine whether the click was in A element. Strange.
The final approach (I hope)
This one should work finally. And I think this is the way it should be done, especially that it allows any structure of the HTML. The are two ways:
1. Convert links within HTMLPanel
This one will find all A elements and convert them into Anchors. It ignores href attribute, but you can add it easily :)
HTMLPanel html = new HTMLPanel("<p>Multilink example 2: <a>link1</a> and <a>link2</a></p>");
NodeList<Element> anchors = html.getElement().getElementsByTagName("a");
for ( int i = 0 ; i < anchors.getLength() ; i++ ) {
Element a = anchors.getItem(i);
Anchor link = new Anchor(a.getInnerHTML());
link.addClickHandler(...);
html.addAndReplaceElement(link, a);
}
2. Insert links into prepared spots
Just insert placeholders, where the widgets should be inserted. You could also use the addAndReplaceElement() method but with string ID.
Anchor a1 = new Anchor("a1");
a1.addClickHandler(...);
Anchor a2 = new Anchor("a2");
a2.addClickHandler(...);
HTMLPanel html = new HTMLPanel("<p>Multilink example: <span id='a1'></span> and <span id='a2'></span></p>");
html.add(a1, "a1");
html.add(a2, "a2");
Try something like this.
For your web page, you can use UiBinder:
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"
xmlns:g="urn:import:com.google.gwt.user.client.ui">
<g:HTMLPanel ui:field="panel">
<p>
GWT is a great tool and I think it will be my preferred tool to
develop web applications. To check out my samples
<g:Anchor ui:field="myLink" text="click here" />
</p>
</g:HTMLPanel>
</ui:UiBinder>
Notice that I've replaced your tag with an Anchor widget. There is also a Hyperlink widget, which has hooks into the history system.
The Anchor has a id of "myLink", which is used in the GWT companion to the XML file:
public class So extends Composite {
private static SoUiBinder uiBinder = GWT.create(SoUiBinder.class);
interface SoUiBinder extends UiBinder<Widget, So> {
}
#UiField
Anchor myLink;
public So() {
initWidget(uiBinder.createAndBindUi(this));
myLink.addClickHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
GWT.log("caught the click");
}
});
}
}
I've added a ClickHandler that captures and acts on the click event.
The main program is simple:
public class SOverflow implements EntryPoint {
public void onModuleLoad() {
RootLayoutPanel.get().add(new So());
}
}
Run this after and a webpage appears with the text and hyperlink. Click on it and "caught the click" appears in the console window (I'm using Eclipse).
I hope this is what you're after. If not exactly, it might at least give you some ideas of how to attack your problem.

Integrating a GWT Dialog into an existing HTML application

I have a situation where I need to integrate a gwt dialog (which to the best of my understanding is implemented as a div with z-index manipulation) into an existing html page.
There are two scenarios:
1. Which is the preferrable and more complicated is where i give the host html page another page which they embed as an iframe and I work my magic through there (maybe connect somehow to the parent window and plant my dialog I'm not sure).
2. Where I have limited access to the html page and I plant some code there which will load my dialog box.
Any ideas or thoughts on how I can implement these?
I've been working for a few months now with GWT and have found it quite nice although I have stayed far far away from the whole HTML area and until now all my work has been done strictly inside my java classes.
Thanks for any ideas and help handed
Ittai
I'll assume by dialog you mean a popup that is invisible at page load and made visible by, say, a click on something in the existing HTML. A simple strategy to make this happen is wrapping the existing HTML.
I have no experience with option 1. As for 2, all you need to alter in the existing HTML is
adding the JS import, e.g.
<script type="text/javascript" language="javascript" src="/com.your.org.Module/com.your.org.module.client.Module.nocache.js"></script>
then adding an id to some clickable element you want to activate your dialog, e.g.
<button id="launchDialog">Show Dialog</button>
and finally adding an empty div with an id to insert your dialog into the DOM.
<div id="dialog"></div>
Then all you need in your Module is
public class Module implements EntryPoint {
#Override
public void onModuleLoad() {
Button b = Button.wrap(DOM.getElementById("launchDialog"));
b.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
RootPanel panel = RootPanel.get("dialog");
Widget w = ... // your dialog widget here
panel.add(w);
}
});
}
}
Lastly, you can play with the visibility of your popup div with the "display: none" style and the show() and hide() methods on the widget.