SkillAgentSearch skills...

JsBind.Net

Create binding from .Net to JavaScript

Install / Use

/learn @mingyaulee/JsBind.Net
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

JsBind.Net

Nuget GitHub Workflow Status Sonar Tests Sonar Quality Gate

A package for creating binding from .Net to JavaScript.

How to use this package

This package can be used in two ways.

Create a binding library

This is the case when a binding library can be created to be consumed by other projects or published to NuGet. You would want to do this if the binding is for common JS libraries (e.g. jQuery) or JavaScript API (e.g. File API and Storage API).

First thing that you will need to do is decide on what is the strategy that you would want to publish your library.

The best approach is to separate the library into multiple packages:

  1. WebAssembly bindings
  2. WebAssembly dependency injection extension
  3. Server bindings
  4. Server dependency injection extension

Of course this can also be separated into just 2 packages:

  1. WebAssembly bindings (with or without dependency injection extension)
  2. Server bindings (with or without dependency injection extension)

To get started with creating a WebAssembly or Server binding project:

  1. Create a new class library project.

  2. Install JsBind.Net from Nuget.

  3. Add PrivateAssets="contentfiles" to the package reference in the csproj project file. For example:

    <PackageReference Include="JsBind.Net" Version="x.x.x" PrivateAssets="contentfiles" />

    This is so that the projects consuming your binding library will also include the build assets in the JsBind.Net package.

  4. Start creating bindings. You can refer to the section How to create bindings below.

To create a dependency injection extension project:

  1. Create a new class library project.
  2. Install JsBind.Net.Extensions.DependencyInjection from Nuget.
  3. Create the extension method for registering the binding services.

Create bindings in a web project directly

When you want to interop to the JavaScript libraries in your project from .Net, you can simply create the bindings required and use it.

  1. Install JsBind.Net (if you intend to use the binding without dependency injection) or JsBind.Net.Extensions.DependencyInjection from Nuget.
  2. Create the bindings, you can refer to the section How to create bindings below.
  3. Register the bindings in the dependency container (if you are using dependency injection).
    services.AddJsBind();
    // if you need this class to be injected to be used
    services.AddTransient<MyBindingClass>();
    
  4. Add the script tag as shown in the section below to import the JsBind.Net JavaScript file.

For projects using the binding without Blazor

The projects using the binding from a binding library or from the project itself will need to include the JavaScript file from JsBind.Net with

<script src="_content/JsBind.Net/JsBindNet.js"></script>

How to create bindings

The test binding project showcases how to create bindings for:

  1. WebAssembly (synchronous)
  2. Server (asynchronous)
  3. WebAssembly dependency injection extension
  4. Server dependency injection extension

For simplicity the test binding project is not separated into multiple projects as advised in the project separation strategy above.

Binding classes

You can start creating binding from the root object that needs to be bound, for example the storage API:

public class LocalStorage : ObjectBindingBase
{
    public LocalStorage(IJsRuntimeAdapter jsRuntime)
    {
        SetAccessPath("localStorage");
        Initialize(jsRuntime);
    }

    public string GetItem(string key) => Invoke<string>("getItem", key);
    public string SetItem(string key, string value) => Invoke<string>("setItem", key, value);
    public string RemoveItem(string key) => Invoke<string>("removeItem", key);
    public void Clear() => InvokeVoid("clear");
}

As for the asynchronous version of the binding, all the return type has to be wrapped in ValueTask and the Async version of Invoke, InvokeVoid and GetProperty has to be used instead.

public class LocalStorage : ObjectBindingBase
{
    public LocalStorage(IJsRuntimeAdapter jsRuntime)
    {
        SetAccessPath("localStorage");
        Initialize(jsRuntime);
    }

    public ValueTask<string> GetItem(string key) => InvokeAsync<string>("getItem", key);
    public ValueTask<string> SetItem(string key, string value) => InvokeAsync<string>("setItem", key, value);
    public ValueTask<string> RemoveItem(string key) => InvokeAsync<string>("removeItem", key);
    public ValueTask Clear() => InvokeVoidAsync("clear");
}

The simplest way is to inherit from ObjectBindingBase class which offers the following APIs:

| API | Description | | ------------------------------------ | ----------------------------------------------------------------------------------------------------- | | SetAccessPath | Sets the access path of the object relative to the globalThis variable. | | GetProperty/GetPropertyAsync | Gets a property value from the JavaScript object with the specified property name. | | SetProperty/SetPropertyAsync | Sets a property value to the JavaScript object with the specified property name. | | Invoke/InvokeAsync | Invoke a function matching the specified function name to the JavaScript object with return value. | | InvokeVoid/InvokeVoidAsync | Invoke a function matching the specified function name to the JavaScript object without return value. | | ConvertToType/ConvertToTypeAsync | Converts the current object to the specified type. |

Binding class constructor

If the binding class can be used directly to perform interop, meaning they can be accessed from the top level globalThis (e.g. globalThis.document/globalThis.window/globalThis.jQuery), the binding class needs a constructor that receives the IJsRuntimeAdapter to be able to interop to JavaScript. The constructor has to use the SetAccessPath API to set the path relative to the globalThis variable. Example of constructor:

public LocalStorage(IJsRuntimeAdapter jsRuntime)
{
    SetAccessPath("localStorage");
    Initialize(jsRuntime);
}

public Window(IJsRuntimeAdapter jsRuntime)
{
    SetAccessPath("window");
    Initialize(jsRuntime);
}

If the binding class represents the structure of objects that can be returned from a JavaScript interop, it needs to have an parameterless constructor, or a constructor that can be deserialized (e.g. decorated with JsonConstructor attribute).

// Location class representing the object returned from window.location
public class Location
{
    // Initialized from JSON deserialization
    public Location()
    {
    }

    [JsonPropertyName("href")]
    public string Href { get; set; }
}

Binding attributes

You should use the binding attributes to define the behaviour of the interop and serialization/deserialization.

Attributes from the System.Text.Json can be used for the serialization and deserialization behaviour.

Attributes that can be used for binding are

| Attribute | Usage | Description | | ------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------- | | BindDeclaredPropertiesAttribute | Class | Include the public properties with setter that are declared in the class for binding. | | BindAllPropertiesAttribute | Class | Include all properties from the JavaScript object for binding. | | BindIncludePropertiesAttribute | Class | Include the specified properties from the JavaScript object for binding. | | BindExcludePropertiesAttribute | Class | Include all properties except for the specified properties from the JavaScript object for binding. | | BindIgnoreAttribute | Property | Ignore this property from binding (Only when the class is decorated with BindDeclaredPropertiesAttribute). |

Properties decorated with JsonIgnoreAttribute and BindIgnoreAttribute will be excluded from binding.

Dynamic binding class

There are cases where you may want to create a binding dynamically, either from an existing ObjectBindingBase instance, or just simply from an access path. For example, to achieve this in JavaScript:

const newDiv = document.createElement("div");
document.body.append(newDiv);

The equivalent code would be:

var document = Any.From("document", jsRuntime);
var newDiv = document.InvokeFunction<Any>("createElement", "div");
document["body"].InvokeFunctionVoid("append", newDiv);

Object reference disposal

Objects returned from function invocation are

View on GitHub
GitHub Stars17
CategoryDevelopment
Updated4mo ago
Forks1

Languages

C#

Security Score

92/100

Audited on Nov 14, 2025

No findings