A binding generator for JS, Call any JS function or property in Blazor Wasm and Server (Including MAUI hybrid) without writing JS wrappers.
- very tiny Overhead ~13kb
- No need to write JS wrappers
- Support for Callbacks
- Write JS code in C#
- WASM and Server Supported
- automatic memory management
Use Nuget Package Manager or .Net CLI
dotnet add package BlazorBindGen
- on top of razor page add Import statements
@inject IJSRuntime runtime
@using BlazorBindGen
@using static BlazorBindGen.BindGen
@using JSCallBack=System.Action<BlazorBindGen.JObjPtr[]>; //optional Typedef
- Intitialize the BindGen
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
await InitAsync(runtime);
}
//on Server
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (!firstRender)
return;
await InitAsync(runtime);
}
-> On Server use Async Version of functions (non async functions will throw `PlatformNotSupportedException`)
// js equivalent
await import("https://unpkg.com/ml5@latest/dist/ml5.min.js");
//c# side
await ImportAsync("https://unpkg.com/ml5@latest/dist/ml5.min.js");
// js equivalent, importing modules
let ml5=await import("https://unpkg.com/ml5@latest/dist/ml5.min.js");
//c# side
var ml5Ptr=await ImportRefAsync("https://unpkg.com/ml5@latest/dist/ml5.min.js");
//js side
var audio=new Audio(param);
//c# side code
var_audio=Window.Construct("Audio",param); /* js reference to Audio Player */
//js equivalent
alert("Hello");
//code to call alert in C#
Window.CallVoid("alert","hello");
//js equivalent
var video = document.querySelector("#videoElement");
//here document is property of window , and dcument has function querySelector
//c# code
var video = Window["document"].CallRef("querySelector", "#videoElement");
//["documemnt"] will return reference to Property document of window , another way to write it is
JObjPtr video = Window.PropRef("document").CallRef("querySelector", "#videoElement");
//CallRef function calls JS function and Returns a reference to it, instead of whole object
// equivalent js code
var ctx = c.getContext("2d");
var grd = ctx.createRadialGradient(75, 50, 5, 90, 60, 100);
ctx.fillStyle = grd;
//c# side
var ctx=canvas.CallRef("getContext","2d");
var grad = ctx.CallRef("createLinearGradient", 0,0,400,0);
ctx.SetPropRef("fillStyle",grad);
//assign a reference to grad(a JobjPtr reference) to property fillStyle of canvas context
//js
var audio=new Audio();
audio.currentTime=6; //set
console.log(audio.currentTime); //get
//c# equivalent
JObjPtr _audio=Window.Construct("Audio"); /* js reference to Audio Player */
public double CurrentTime
{
get => _audio.PropVal<double>("currentTime");
set => _audio.SetPropVal("currentTime", value);
}
//js equivalent
var audio=new Audio();
audio.onloadeddata=()=>{ console.log("loaded"))};
//cs equivalent
{
var _audio=Window.Construct("Audio"); /* js reference to Audio Player */
_audio.SetPropCallBack("onloadedmetadata", (_) => OnLoadedMetaData?.Invoke(this));
}
public delegate void LoadedMetaDataHandler(object sender);
public event LoadedMetaDataHandler OnLoadedMetaData;
Be sure to check out sampleApp for more examples
@page "/ml5"
@using BlazorBindGen
@using static BlazorBindGen.BindGen
@using JSCallBack=System.Action<BlazorBindGen.JObjPtr[]>; //optional only needed to simplify callback type name
@inject IJSRuntime runtime
@if (isLoaded)
{
<input type="text" class="bg-dark text-white border-light" @bind="predictText" placeholder="write review here " style="font-size:18px"/>
<button class="btn btn-primary" id="mbtn" @onclick="Predict">Predict</button><br /><br />
if(score>0)
{
<div class="alert alert-primary">
<p>Review: @GetEmoji() <br />Score: @score</p>
</div>
}
}
else
{
<div class="alert alert-warning">
Fetching Movie Review Dataset (~16 MB)
</div>
}
@code
{
JWindow win;
public JObjPtr sentiment;
string predictText;
bool isLoaded = false;
double score;
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
await Init(runtime);
win = Window;
await ML5Init();
}
async Task ML5Init()
{
await Import("https://unpkg.com/ml5@latest/dist/ml5.min.js");
Console.Clear();
var ml5 = win["ml5"];
sentiment = ml5.CallRef("sentiment", "movieReviews", (JSCallBack)OnModelLoaded);
}
void Predict()
{
var v = sentiment.Call<Score>("predict", predictText);
score=v.score;
StateHasChanged();
}
void OnModelLoaded(params JObjPtr[] args)
{
isLoaded = true;
StateHasChanged();
}
string GetEmoji()
{
if (score > 0.7)
return "😀";
else if (score > 0.4)
return "😐";
else
return "😥";
}
record Score(double score);
}
Above example binding could also be generated with Source Generator for WASM .
using BlazorBindGen.Attributes;
namespace SampleApp.JSBinding
{
[JSWindow] // represents JS Window class
public static partial class DomWindow
{
[JSProperty]
private static ML5 ml5; // refers to window.ml5 prop js side
}
[JSObject("https://unpkg.com/ml5@latest/dist/ml5.min.js")]
public partial class ML5
{
[JSFunction("sentiment")]
public partial Sentiment Sentiment(string modelName,OnModelLoadHandler onModelLoad);
[JSCallback]
public delegate void OnModelLoadHandler();
[JSConstruct("p5")]
public partial Sentiment Construct();
}
[JSObject]
public partial class Sentiment
{
[JSFunction("predict")]
public partial Score Predict(string text);
}
public record Score(double score);
}
see usage here
- BlazorBindGen Api is subject to change, API is not stable.
- Note: Blazor Server requires use of Async functions otherwise UI thread will be blocked by it or alternatively you can call BindGen functions on different thread
#issue.