Aspnetcore: Make DOM element JavaScript lifecycle callbacks declarable in Razor syntax

Created on 2 Dec 2019  路  4Comments  路  Source: dotnet/aspnetcore

Inspired by @SQL-MisterMagoo's issue #17472, I suggest the following generalized solution:

Describe the solution you'd like

When defining a HTML element to be rendered in the browser in Blazor, it should be possible to specify JavaScript functions that are executed in the context of that HTML element, when it is first rendered, rendered, and disposed/removed from the DOM.

For example, the Dropdown Bootstrap component could look like this then (this is just for illustration purposes, not a complete implementation):

<div class="dropdown">
  <button @onFirstRender="boostrapInteropt.dropdownInit(@dotnetHelper)" 
          @onRender="boostrapInteropt.dropdownRender()"
          @onDispose="boostrapInteropt.dropdownDispose()" 
          class="btn btn-secondary dropdown-toggle" ...>
    Dropdown button
  </button>
  <div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
    <a class="dropdown-item" href="#">Action</a>
    <a class="dropdown-item" href="#">Another action</a>
    <a class="dropdown-item" href="#">Something else here</a>
  </div>
</div>

@code {
    private DotNetObjectReference dotnetHelper = DotNetObjectReference.Create(...);
}

where this in each function call is bound to the rendered DOM element. This assumes an JavaScript object exists such as:

 window.boostrapInteropt { 
   dropdownInit: function(dotnetHelper) {
        $(this).dropdown();
        // ...
   },
   dropdownRender: function() { 
        $(this).dropdown('update');
        // ...
   },
   dropdownDispose: function() { 
        $(this).dropdown('dispose');
        // ...
   }
}

Note, it should be possible to pass arguments to the JavaScript functions as it is through the normal JsRuntime's invoke methods. In the example above, we are passing a dotnetHelper callback object.

This will enable simple and consistent integration with existing JavaScript based components, that usually surface methods such as init and destroy/dispose. It ensures JavaScript objects are correctly cleaned up and functionality is available as soon as possible to the end user.

Why not use razor lifecycle methods and JsRuntime.InvokeAsync(...)?

The problem with OnAfterRender is that it is first called after the element is visible to the users. In Blazor Server scenarios, the round-trip network delay can be high enough, that the user will experience elements not working probably or visually moving on the screen, e.g. if using popper.js to position elements.

affected-medium area-blazor enhancement severity-minor

All 4 comments

We've moved this issue to the Backlog milestone. This means that it is not going to happen for the coming release. We will reassess the backlog following the current release and consider this item at that time. However, keep in mind that there are many other high priority features with which it will be competing for resources.

That is fair @javiercn.

One additional point I would like to make is that this will make it easier to integrate existing JavaScript components with Blazor, which will likely help increase adoption of Blazor. The important point is that it will make it possible to make the integration consistent between Blazor Server and Blazor Wasm.

That should give us plenty of time to work on a proof of concept 馃榿
I've got a very basic version working now, we can always provide an alternative js file for the brave ones to play with.

Why not use razor lifecycle methods and JsRuntime.InvokeAsync(...)?

The problem with OnAfterRender is that it is first called after the element is visible to the users.

鈽濓笍 This is an important comment. Being able to run JavaScript for an element directly after it鈥檚 rendered would be amazing.

Was this page helpful?
0 / 5 - 0 ratings