dotnet / aspnetcore

ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.
https://asp.net
MIT License
35.19k stars 9.93k forks source link

Add OnRenderComplete lifecycle method #16250

Closed muqeet-khan closed 4 years ago

muqeet-khan commented 6 years ago

Scenario:

ExampleComponent:

<div id="@DivId">
</div>
@functions(){
    public string DivId {get;set;} = "componentDiv";
    protected override void OnParametersSet()
    {
        RegisteredFunction.Invoke<bool>("initializeSomeJSFunction", new[] { DivId });
    }
}

If the initializeSomeJSFunction in JS wants to access the div componentDiv at this point, it can't as the component has not yet rendered and is therefore not accessible by document.getElementById('componentDiv'). This is blocking porting existing JS libraries to Blazor, if the library requires that context.

If there was an override when the render is completed, then, I assume the elements will be visible to the DOM and can be used within the registeredFunction. If this is by design, what is the recommended approach to tackle a scenario like this?

SteveSandersonMS commented 6 years ago

I agree. It's been in the back of my mind for a while that we'll need this. I agree it's important for integrating with external JS libraries.

We also need some way of giving the Blazor component a way of referencing its own DOM element in this callback.

SteveSandersonMS commented 6 years ago

One important caveat is that this "After render" callback is going to be called asynchronously. That is, the browser will have actually painted its DOM elements and then we will call your "after render" callback (usually just about instantly). This is inevitable if we want to run the .NET code in a WebWorker, since all communication between the host frame and the web worker is async.

@muqeet-khan Do you think that will be OK for your scenarios?

muqeet-khan commented 6 years ago

@SteveSandersonMS Yes, as long as the elem is visible to the DOM at that moment, that's all I need.

As a side note, here's another crazy land option I landed in yesterday:

JS:

Blazor.RegisterFunction('InitializeJS',function(data){
let elem = document.createElement('p')
elem.setAttribute('id','myNewParagraph');
//also do something useful with data and append it to elem perhaps
return elem;
});

MyComponent.cshtml:

<div>
@Html.Raw(_modifiedBlob)
</div>
@functions{
 public string SomeBlobOfData {get;set;} = "";
  private string _modifiedBlob {get; private set;} = "";
 protected override OnInit()
 {
   var result = RegisteredFunction.Invoke<RenderFragment\HTMLContent\string>('initializeJS');
   // this is trying to refer to the current component. Or may be the render tree?   
   //this.appendDomElem(result);
   // or store in some string and display the string as Raw in the component somewhere.
   _modifiedBlob = result;
 }
}

As an example of this consider something like HighlightJS, where the component may give the registered function a blob of code and hljs will "process" that blob and return the highlighted elems.

May be above is not even needed when aspnet/Blazor#167 is implemented and we could just return string from function and hold the result and use it as @HTML.Raw(SomeBlobOfData) is available.

DNF-SaS commented 6 years ago

I think this is very important, because rendering new elements into the DOM and wiring up events afterwards is pretty common. Think of a modal popup with content from blazor component(s) which calls back via an JS-event when it's been closed.

SteveSandersonMS commented 6 years ago

Closing in favour of aspnet/Blazor#691. Note that in aspnet/Blazor#691 I implemented the callback synchronously for consistency with everything else, but there is a reasonable chance we're going to make this and all JS interop async at some point if we implement web worker support.

muqeet-khan commented 6 years ago

@SteveSandersonMS just FYI... OnAfterRender is working beautifully just as we wanted... Here's a sneak peek ...

image

SteveSandersonMS commented 6 years ago

Looks fantastic! Have you published the charts component in a NuGet package?

galvesribeiro commented 6 years ago

Yeah! BlazorMaterial components also thank you for that @SteveSandersonMS

All the hacky JS was removed and the events are being used instead. Keep going! :)

muqeet-khan commented 6 years ago

@SteveSandersonMS Yes, just did. Install-Package BlazorComponents -Version 0.1.0 this version is compatible with 0.3.0