SerratedSharp.JSInteropHelpers 0.1.6

Prefix Reserved
There is a newer version of this package available.
See the version list below for details.
dotnet add package SerratedSharp.JSInteropHelpers --version 0.1.6                
NuGet\Install-Package SerratedSharp.JSInteropHelpers -Version 0.1.6                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="SerratedSharp.JSInteropHelpers" Version="0.1.6" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add SerratedSharp.JSInteropHelpers --version 0.1.6                
#r "nuget: SerratedSharp.JSInteropHelpers, 0.1.6"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install SerratedSharp.JSInteropHelpers as a Cake Addin
#addin nuget:?package=SerratedSharp.JSInteropHelpers&version=0.1.6

// Install SerratedSharp.JSInteropHelpers as a Cake Tool
#tool nuget:?package=SerratedSharp.JSInteropHelpers&version=0.1.6                

SerratedSharp.JSInteropHelpers

A class library to simplify creating .NET WASM wrappers on JS instances. This library is leveraged by SerratedJQ, but is designed to be agnostic. Otherwise it has not been thoroughly refined, but may be of use to others.

Example

This demonstrates how easy it is to create a C# interface for a large API surface area for a JS instance. In this case various methods for a jQuery object:

public class JQueryPlainObject : IJSObjectWrapper<JQueryPlainObject>
{
    //...
    public JQueryPlainObject First() => this.CallJSOfSameNameAsWrapped();
    public bool Is(string selector) => this.CallJSOfSameName<bool>(selector);
    public JQueryPlainObject Eq(int index) => this.CallJSOfSameNameAsWrapped(index);
    public JQueryPlainObject Slice(int start, int end) => this.CallJSOfSameNameAsWrapped(start, end);
    public JQueryPlainObject Filter(string selector) => this.CallJSOfSameNameAsWrapped(selector);        
    public JQueryPlainObject Odd() => this.CallJSOfSameNameAsWrapped();
    public JQueryPlainObject NextUntil(string stopAtSelector = null, string filterResultsSelector = null)
    public JQueryPlainObject Add(JQueryPlainObject jqObject) => this.CallJSOfSameNameAsWrapped(jqObject);
    public JQueryPlainObject Add(string selector, JQueryPlainObject context) => this.CallJSOfSameNameAsWrapped(selector, context);
    public JQueryPlainObject After(string html, params string[] htmls) 
        => this.CallJSOfSameNameAsWrapped(Params.Merge(html, htmls));
    public JQueryPlainObject After(JQueryPlainObject jqObject, params JQueryPlainObject[] jqObjects) 
        => this.CallJSOfSameNameAsWrapped(Params.Merge(jqObject, jqObjects));
    public JQueryPlainObject Append(IJQueryContentParameter contentObject, params IJQueryContentParameter[] contentObjects) 
        => this.CallJSOfSameNameAsWrapped(Params.PrependToArray(contentObject, ref contentObjects));
    
}

CallJSOFSameName* methods leverage [CallerMemberName] to determine name of containing C# function, convert it to lower camel case, then call a method of that name on this's containing JSObject. The instance wrapper should implement IJSObjectWrapper<W> where W is the wrapping type, and holds a reference to the JSObject instance that it wraps. A C# call such as instance.Filter(index) would be translated to the javascript jsObject["filter"].apply(jsObject, index); which is a generic approach equivilant to jsObject.filter(index);.

Note in the above examples, use of Params.Merge and Params.PrependToArray which is necesary in some cases where the C# params and Javascript params differ in regards to repeating params.

A wrapper would contain a reference to a JSObject, which is the .NET handle for the javascript object it wraps:

public class JQueryPlainObject : IJSObjectWrapper<JQueryPlainObject>
{
    internal JSObject jsObject;// reference to the jQuery javascript interop object
    
    /// <summary>
    /// Handle to the underlying javascript jQuery object
    /// </summary>
    public JSObject JSObject { get { return jsObject; } } // required by IJSObjectWrapper and is the handle used by CallJSofSameName* methods

    // Not required, but often JS libraries return instances through specific static calls(not shown), and therefore we do not provide a public default constructor.
    // It is internal so our other static methods that capture instance to be wrapped can call this constructor.
    internal JQueryPlainObject() { }

    // Instances can only be created thru factory methods like Select()/ParseHtml() or .WrapInstance() used when an interop *AsWrapped call returns a new JSObject.
    
    public JQueryPlainObject(JSObject jsObject) { this.jsObject = jsObject; }

    // This static factory method defined by the IJSObjectWrapper enables generic code such as CallJSOfSameNameAsWrapped to automatically wrap JSObjects
    static JQueryPlainObject IJSObjectWrapper<JQueryPlainObject>.WrapInstance(JSObject jsObject)
    {
        return new JQueryPlainObject(jsObject);
    }
    //...

The above helpers assist with instance method mapping without needing any javascript shims or manual JSImport mapping.

.NET's JSImport is still used for static methods.

For example, a static javascript method that returns new instances could be called like so to wrap them in a new C# wrapper:

   YourWrapperObject newInstance = YourWrapperObject.Select("#bob");

Calling the internal constructor and assigning the JSObject instance it wraps:

public static YourWrapperObject Select(string selector)
{
    var managedObj = new YourWrapperObject();
    managedObj.jsObject = YourWrapper.GetSomething(selector); // a method that returns an instance of the JS type you're wrapping
    return managedObj;
}

//...
[JSImport(baseJSNamespace + ".GetSomething", moduleName)]
public static partial JSObject GetSomething(string selector);
JQueryProxy.GetSomething = function (selector) {
    return someJSlibrary().getSomething(selector);
};

As a more concrete example, SerratedJQ captures jQuery object instances from static .Select calls like so to wrap them:

// Return new wrapped instance from the JSObject instance returned by the static JQueryProxy.Select
public static JQueryPlainObject Select(string selector)
{
    var managedObj = new JQueryPlainObject();// use internal constructor
    managedObj.jsObject = JQueryProxy.Select(selector); // assign returned JS object reference to its jsObject field
    return managedObj; // return newly wrapped instance
}
// Static proxy
[JSImport(baseJSNamespace + ".Select", moduleName)]
public static partial JSObject Select(string selector);
// JS Shim for static call
JQueryProxy.Select = function (selector) {
    return jQuery(document).find(selector);
};

Prerequisites

  • This can be leveraged where the final downstream consuming assembly will be either a .NET 8 WASMBrowser assembly or Uno.Wasm.Bootstrap assembly.
  • .NET 8 Core
Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net8.0

    • No dependencies.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on SerratedSharp.JSInteropHelpers:

Package Downloads
SerratedSharp.SerratedJQ

A C# WebAssembly wrapper for jQuery, intended to enable implementation of client side logic in C# for a traditional web application such as ASP.NET MVC. Provides the capability to read and manipulate the HTML DOM, create .NET event handlers subscribed to HTML DOM events, hold references to DOM elements from a .NET WebAssembly, and attach primitive data or managed object references to elements. Intended for us in assemblies that leverage Uno.Wasm.Bootstrap for compilation to WebAssembly format, but does not require consumers to use the full Uno Platform.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
0.1.7 76 7/3/2024
0.1.6 107 3/27/2024
0.1.3 185 12/12/2023