SerratedSharp.SerratedJQ 0.0.4

Prefix Reserved
There is a newer version of this package available.
See the version list below for details.
dotnet add package SerratedSharp.SerratedJQ --version 0.0.4                
NuGet\Install-Package SerratedSharp.SerratedJQ -Version 0.0.4                
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.SerratedJQ" Version="0.0.4" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add SerratedSharp.SerratedJQ --version 0.0.4                
#r "nuget: SerratedSharp.SerratedJQ, 0.0.4"                
#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.SerratedJQ as a Cake Addin
#addin nuget:?package=SerratedSharp.SerratedJQ&version=0.0.4

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

SerratedJQ

A C# WebAssembly wrapper for JQuery which provides the capability to read and manipulate the HTML DOM, create .NET event handlers for HTML DOM events, hold references to DOM elements from C# WASM, attach data or managed references to HTML DOM element datasets, and expose static .NET methods as javascript methods. Leverages Uno.Wasm.Bootstrap for WebAssembly support, but does not require consumers to use full Uno Platform. The intention is that this wrapper would be used by those building traditional web applications(e.g. ASP.NET MVC) but who wish to use a .NET language such as C# to implement client side UI logic rather than javascript. Please see Nuget package Release Notes for specific version compatibility information.

Demo

A demo is published as a static site at https://serratedsharp.github.io/CSharpWasmJQueryDemo/

Emphasis on "static". There's no server side code in this demo. The .NET assemblies are downloaded to your browser as simple static files, the same way your browser would download *.js, *.css, or images, and run inside a WebAssembly sandbox. No .NET server side hosting is needed, but this approach could easily be combined with any traditional web application such as MVC. This makes this solution composable with existing architectures looking to provide greater agility in developing client side logic.

A more extensive demo including integration with a MVC project and API requests from the WASM client to MVC host, including a walkthrough of the code: https://www.youtube.com/watch?v=0BrGf99K6CU

Code from Demo: https://github.com/SerratedSharp/SerratedJQ/tree/main/SerratedJQSample

Example

This example C# WebAssembly code shows how you might select an HTML element, subscribe to an HTML click event, and respond to the event by manipulating the DOM, such as appending an element to the page.

using SerratedSharp.SerratedJQ;
static void Main(string[] args)
{
  var clickMe = JQueryBox.FromHtml("<span>Click Me</span>");
  JQueryBox.Select("body").Append(clickMe);
  clickMe.OnClick += Test_OnClick;
}

void Test_OnClick(JQueryBox sender, dynamic e)
{
  var newElement = JQueryBox.FromHtml("<span>Clicked</span>");
  JQueryBox.Select("body").Append(newElement);
}

Installation

Prerequisites

  • SerratedSharp.SerratedJQ, Uno.Wasm.Bootstrap, Uno.Foundation.Runtime.WebAssembly
  • .NET Core 7

Quick Start Guide

  • Create a Blank Solution, add new .NET Console App (.NET 7) and ASP.NET Core Web App (Model-View-Controller) projects.
  • Build the MVC project
  • Add Nuget references to Uno.Wasm.Bootstrap, Uno.Foundation.Runtime.WebAssembly, and SerratedSharp.SerratedJQ in the Console project. image
  • Add a copy of this Build.props file to the Console app: Build.props
  • Update <DestinationWebProjectName> to match your MVC app's project folder name, then add <Import Project=".\Build.props" /> inside the Console app's *.csproj just within the </Project> closing tag.
  • Place the following in the MVC project's Views/Shared/_Layout.cshtml in the bottom of the <head> tag, adjusting the jquery URL as appropriate for your inclusion approach. Note the below includes loading the jQuery javascript library:
    <script src="https://code.jquery.com/jquery-3.7.1.min.js" integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" crossorigin="anonymous"></script>

    @inject Microsoft.AspNetCore.Hosting.IWebHostEnvironment WebHostEnvironment
    @{
        var directories = new System.IO.DirectoryInfo(WebHostEnvironment.WebRootPath).GetDirectories("package_*").OrderByDescending(d => d.CreationTimeUtc);
        string wasmPackageName = directories.First().Name;
        string wasmBaseUrl = $"{Url.Content("~/")}{wasmPackageName}";// Get most recently generated WASM package and reference it.
        // Note you must also set the web project's folder name in the build property <DestinationWebProjectName>. See Sample.Wasm/Build.props
    }
    <script type="text/javascript" src="@wasmBaseUrl/require.js"></script>    
    <script type="text/javascript" src="@wasmBaseUrl/uno-bootstrap.js"></script>    
    <link rel="stylesheet" type="text/css" href="@wasmBaseUrl/normalize.css" />
    <link rel="stylesheet" type="text/css" href="@wasmBaseUrl/uno-bootstrap.css" />
  • Place the following just after the ending </header>
    <div id="uno-body" class="container-fluid uno-body">
        <div class="uno-loader"
             loading-position="bottom"
             loading-alert="none">

            
            <img class="logo"
                 src=""
                 title="Uno is loading your application" />

            <progress></progress>
            <span class="alert"></span>
        </div>
    </div>
    <noscript>
        <p>This application requires Javascript and WebAssembly to be enabled.</p>
    </noscript>
  • In Startup.cs, preceding the existing app.UseStaticFiles(); add:
var provider = new Microsoft.AspNetCore.StaticFiles.FileExtensionContentTypeProvider();
provider.Mappings[".clr"] = "application/octet-stream";
provider.Mappings[".dat"] = "application/dat";
app.UseStaticFiles(new StaticFileOptions { ContentTypeProvider = provider });
  • Build both projects, then launch the MVC project.
  • If everything is working properly then you should see the Console.Writeline "Hello World" appear as message in the browser debug console, confirming your C# ran locally in the browser.

[!NOTE] You must explicitly build the WasmClient when making changes so it rebuilds the package. Because there is no project reference from the MVC project to the WasmClient project, then it is not automatically rebuilt.

Overview

This setup will generate the WebAssembly when the Console project is compiled and copy it into the wwwroot of the ASP.NET project. When the ASP.NET project is launched and a page loads in the browser, then Uno Bootstrap will download and run our WebAssembly in the browser. The #uno-body div displays a loading progress bar when downloading/initializing the WASM. Typically issues with this process as well as exceptions generated from your WebAssembly will appear in the browser console.

Usage

CallbacksHelper.Export(jsMethodName: "IndexPageReady", () => IndexClient.Init());// register a JS to C#WASM callback
Uno.Foundation.WebAssemblyRuntime.InvokeJS("WasmReady()");// signal WASM as loaded/ready
// In page specific CSHTML, wait for both JQuery and WASM to load
@section Scripts {
    <script type="text/javascript">        
        function WasmReady() { // Wait for WASM to initialize and start Program.Main()
            $(function () { // Wait for JQuery to be ready
                Serrated.Callbacks.IndexPageReady(); // Initialize this page's script.
            });
        }
    </script>
}

Warning

ManagedObjectAttach() is experimental and potentially generates memory leaks due to shortcuts taken to pin managed objects referenced from DOM or javascript.

Security Considerations

The same security considerations when using JQuery apply when using this wrapper. Some JQuery methods could be vulnerable to XSS where uncleaned data originating from different users is passed into library methods. (This is not a unique risk to JQuery, and applies in some form to virtually all templating and UI frameworks where one might interpolate user data and content.) See Security Considerations in https://api.jquery.com/jquery.parsehtml/ and https://cheatsheetseries.owasp.org/cheatsheets/DOM_based_XSS_Prevention_Cheat_Sheet.html to understand the contexts where different sanitization must occur. Typically this means the appropriate encoding or escaping is applied to HTML or Javascript, depending on the context of where the user generated content is being interpolated.

Release Notes

0.0.3

Updated to latest stable Uno.Wasm.Bootstrap package.

0.0.2

Appropriate encoding applied to ensure parameters used in the javascript interopt layer cannot break out of the parameter context. This addresses remaining security concerns regarding javascript generated in the interopt layer.

0.0.1-alpha.5

Implemented automatic management of pinning/unpinning event listeners to ensure managed listeners are made eligible for garbage collection when no unmanaged JS handles/event publishers reference them.

0.0.1-alpha.4

The event object is now passed to handlers as a C# dynamic type allowing those who know the structure to navigate to desired values. I would recommend favoring using the JQueryBox sender over the loosely typed dynamic event where there is overlap, such as retrieving the value on an input event. Support is accomplished through a serialization/deserialization since references cannot be passed across the WASM boundary.
This means properties such as e.originalEvent.target are an object ID rather than an object reference.

void Test_OnClick(JQueryBox sender, dynamic e)
{
  Console.WriteLine(e); // Outputs full event structure to browser debug console
  string eventName = e.type;// If we know the structure of the event object we can access values through loosely typed dynamic
  Assert.Equal(eventName == "click");
}
Product Compatible and additional computed target framework versions.
.NET net7.0 is compatible.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  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.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
0.1.8 61 7/5/2024
0.1.7 76 7/3/2024
0.1.6 92 3/27/2024
0.1.4 166 12/17/2023
0.1.3 109 12/12/2023
0.1.2 136 11/19/2023
0.1.1 107 11/19/2023
0.1.0 100 11/17/2023
0.0.4 129 9/1/2023
0.0.3 130 8/14/2023

This version has been tested with Uno.Wasm.Bootstrap 7.0.27 and Uno.Foundation.Runtime.WebAssembly 4.9.45 under .NET Core 7.