How to use C# WebAssemly from JavaScript without Blazor web components - javascript

To compile C# into WebAssemly and interop with JS it's required to use Blazor WebAssembly ASP.NET framework, which is designed for SPA and contains lot of overhead in case you just want to use a C# library from JS.
What is the minimum setup to just compile a DLL to WebAssembly and use it from JavaScript?

Create a new empty C# project with the following configuration (via .csproj):
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<LangVersion>10</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="6.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="6.0.0" PrivateAssets="all" />
</ItemGroup>
</Project>
Initialize Blazor JS runtime and specify the bindings:
namespace WasmTest;
public class Program
{
private static IJSRuntime js;
private static async Task Main (string[] args)
{
var builder = WebAssemblyHostBuilder.CreateDefault(args);
var host = builder.Build();
js = host.Services.GetRequiredService<IJSRuntime>();
await host.RunAsync();
}
[JSInvokable]
public static async Task<string> BuildMessage (string name)
{
var time = await GetTimeViaJS();
return $"Hello {name}! Current time is {time}.";
}
public static async Task<DateTime> GetTimeViaJS ()
{
return await js.InvokeAsync<DateTime>("getTime");
}
}
Publish with dotnet publish and use the C# library from JS:
<script src="_framework/blazor.webassembly.js" autostart="false"></script>
<script>
window.getTime = () => {
return new Date().toJSON();
};
window.onload = async function () {
await Blazor.start();
const msg = await DotNet.invokeMethodAsync("WasmTest", "BuildMessage", "John");
console.log(msg);
};
</script>
Alternatively, here is a solution, that allows compiling C# project into single-file UMD library, which can be consumed in any JavaScript environment: browsers, node, and custom restricted environments, such as VS Code's web extensions: https://github.com/Elringus/DotNetJS

Related

Blazor include javascript library in specific component

My Blazor server side app has several js / css libraries included in the _Layout.cshtml file. However, in one of my components I want to leverage an additional set of js / css libraries to add functionality for that specific component-- I don't want these libraries to get loaded globally.
Is there a way to do this that is native to Blazor? I've seen many posts that use a third-party library or some sort of hack to accomplish this (for example https://github.com/mishelshaji/DynamicJavaScriptInBlazor), but it seems like something that ought to be supported natively.
Using the lazy loading feature of IJSRuntime you can dynamically load JavaScript with your component.
In this component I lazy load a script file within a Razor Class Library.
Relevant sections from a code behind approach component:
public partial class Dialog : ComponentBase, IAsyncDisposable
{
private readonly Lazy<Task<IJSObjectReference>> moduleTask;
private DotNetObjectReference<Dialog> dotNetObjectReference;
...
public Dialog()
{
moduleTask = new(() => jsRuntime!.InvokeAsync<IJSObjectReference>(
identifier: "import",
args: "./_content/BlazorDialogs/dialogJsInterop.js")
.AsTask());
dotNetObjectReference = DotNetObjectReference.Create(this);
}
[Inject]
private IJSRuntime jsRuntime { get; set; }
...
public async ValueTask ShowDialogAsync()
{
var module = await moduleTask.Value;
await module.InvokeVoidAsync(identifier: "showDialog", dialogElement, dotNetObjectReference);
...
}
public async ValueTask CloseDialogAsync()
{
var module = await moduleTask.Value;
await module.InvokeVoidAsync(identifier: "closeDialog", dialogElement);
...
}
[JSInvokable]
public void OnDialogClosed()
{
...
}
public async ValueTask DisposeAsync()
{
if (moduleTask.IsValueCreated)
{
var module = await moduleTask.Value;
await module.DisposeAsync();
}
}
}
Note: OnDialogClosed is called from the JavaScript.
Repo

Is it possible to compile C# project into single-file UMD JavaScript library?

While Blazor provides C#/JS interop, it only works in browsers and is designed for SPA. Microsoft doesn't seem to plan adding support for other scenarios: https://github.com/dotnet/aspnetcore/issues/37910
Is it possible to use C# programs and libraries in JavaScript without dependency on DOM or other environment-specific APIs?
It's possible with a custom build of .NET WebAssembly runtime and environment-agnostic JavaScript wrapper.
Here is a solution that uses such custom build to allow compiling C# project into UMD library, which can be used in browsers, node.js and custom restricted environments, such as VS Code's web extensions: https://github.com/Elringus/DotNetJS
To use it, specify Microsoft.NET.Sdk.BlazorWebAssembly SDK and import DotNetJS NuGet package:
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="DotNetJS" Version="*"/>
</ItemGroup>
</Project>
To associate a JavaScript function with a C# method use JSFunction attribute. To expose a C# method to JavaScript, use JSInvokable attribute:
using System;
using DotNetJS;
using Microsoft.JSInterop;
namespace HelloWorld;
partial class Program
{
// Entry point is invoked by the JavaScript runtime on boot.
public static void Main ()
{
// Invoking 'dotnet.HelloWorld.GetHostName()' JavaScript function.
var hostName = GetHostName();
// Writing to JavaScript host console.
Console.WriteLine($"Hello {hostName}, DotNet here!");
}
[JSFunction] // The interoperability code is auto-generated.
public static partial string GetHostName ();
[JSInvokable] // The method is invoked from JavaScript.
public static string GetName () => "DotNet";
}
Publish the project with dotnet publish. A single-file dotnet.js library will be produced under the "bin" directory. Consume the library depending on the environment:
Browser
<!-- Import as a global 'dotnet' object via script tag. -->
<script src="dotnet.js"></script>
<script>
// Providing implementation for 'GetHostName' function declared in 'HelloWorld' C# assembly.
dotnet.HelloWorld.GetHostName = () => "Browser";
window.onload = async function () {
// Booting the DotNet runtime and invoking entry point.
await dotnet.boot();
// Invoking 'GetName()' C# method defined in 'HelloWorld' assembly.
const guestName = dotnet.HelloWorld.GetName();
console.log(`Welcome, ${guestName}! Enjoy your global space.`);
};
</script>
Node.js
// Import as CommonJS module.
const dotnet = require("dotnet");
// ... or as ECMAScript module in node v17 or later.
import dotnet from "dotnet.js";
// Providing implementation for 'GetHostName' function declared in 'HelloWorld' C# assembly.
dotnet.HelloWorld.GetHostName = () => "Node.js";
(async function () {
// Booting the DotNet runtime and invoking entry point.
await dotnet.boot();
// Invoking 'GetName()' C# method defined in 'HelloWorld' assembly.
const guestName = dotnet.HelloWorld.GetName();
console.log(`Welcome, ${guestName}! Enjoy your module space.`);
})();

How can I add the external jar file into react native application and use as a plugin into the javascript file for both android and iOS

Actually, I have existing SDKs and I wanted to use that SDK in the react native app.
For android
I tried adding the jar file into the libs folder of /android/app/
Added dependencies into file /android/app/build.gradle
implementation fileTree(include: ['*.jar'], dir: 'libs')
But I did not get how can I use these jar files in my js file. How can I create the object and call the methods?
The main concern is how can I use the external java libraries in my react native app?
You should use native modules them are bridge between JS and native
https://reactnative.dev/docs/native-modules-android
Example
public class DummyModule extends ReactContextBaseJavaModule {
MyDummyClass dummy // this context
public DummyModule(final ReactApplicationContext reactContext){
super(reactContext);
}
#Override
// getName is required to define the name of the module represented in
// JavaScript
public String getName() {
return "DummyModule";
}
#ReactMethod
public void startMyClass() {
this.dummy = new MyDummyClass();
}
#ReactMethod
public void fooActionClass() {
if(this.dummy != null){
this.dummy.fooAction();
}
}
}
In your javascript code
import { NativeModules } from 'react-native';
const dummyModule = NativeModules.DummyModule;
dummyModule.startMyClass();
// Make sure that u call the action when the class is instanciated.
dummyModule.fooActionClass();
Usefull question as well Sending hashmao from java to react native

Static Assets Cache with Spring

I am developing a web application and intend to make use of the performance boost that caching resources give, but it comes with an important caveat. Whenever I updated a static file, users wouldn't see these changes immediately, and so had to disable the browser's cache in order to fetch the newest version. In order to fix this issue, I decided to add static assets versioning. Which works as intended with the following code.
#Override
public void addResourceHandlers(final ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**")
.addResourceLocations("classpath:/static/")
.setCacheControl(CacheControl.maxAge(365, TimeUnit.DAYS))
.resourceChain(true)
.addResolver(new VersionResourceResolver().addContentVersionStrategy("/**"))
// Costume made transformer to handle JS imports
.addTransformer(new JsLinkResourceTransformer())
.addTransformer(new CssLinkResourceTransformer());
}
#Bean
public ResourceUrlEncodingFilter resourceUrlEncodingFilter() {
return new ResourceUrlEncodingFilter();
}
Everything was working as intended, except for one simple detail. JS imports were still loading the none versioned files. So something like import * from './myscrypt.js', would not work properly.
I had to implement my own resource transformer in order to avoid that new caveat. The implementation does it's job, and now my imports would fetch the right version, like import * from './myscript-149shdhgshs.js'. Then, I thought everything was fixed, but a new issue came up. Here is the scenario, which will make it easier to understand.
I load a page that includes script.js
Then Spring serve me with the correct version of the file script-v1.js
After that, script-v1.js imports functions from myscript.js
The browser fetch the right version of the script myscript-v1.js
The two of them get cached locally
I update myscript.js making a new version myscript-v2.js
I reload the page, but since script-v1.js was stored in cache, I load it with the old import myscript-v1.js, even though there is a new version
I just can't seem to make it work. Of course, I could simply stop using js modules and instead just load all the scripts at once, but that is not the solution I want to go for. Would there be a solution for js module versioning using Spring?
My way of solving this cached version will be using app version. If the project is built on Maven, I see you're using classpath resource for static file resolutions. Whenever there is a new change to js file, you will have new build and if you could change the version on every build, here is my workaround would look like.
pom.xml
<version>0.1.0</version>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
application.yml
build:
version: #project.version#
This will push version from pom.xml to application.yml both dev on IDE and built jar
Controller
I'm using mustache view resolver here.
#Controller
public class HelloController {
#Value("${build.version}")
private String version;
private String encodedVersion;
#PostConstruct
public void setup() {
encodedVersion = new String(Base64.getEncoder().encode(version.getBytes())).replace("=", "");
}
#RequestMapping("/home")
public ModelAndView home() {
ModelAndView mv = new ModelAndView();
mv.setViewName("home.html");
return mv;
}
#ModelAttribute("version")
public String getVersion() {
return encodedVersion;
}
}
home.html
<html>
<head>
<script type="text/javascript" src="/pop.js?cache={{version}}"></script>
<script type="text/javascript">
window.version = "{{version}}" // in case you need this somewhere
</script>
</head>
<body>
<h1>Home1</h1>
version: {{version}}
</body>
</html>
Manipulating existing js files
#Configuration
#AutoConfigureAfter(DispatcherServletAutoConfiguration.class)
public class Config implements WebMvcConfigurer {
#Value("${build.version}")
private String version;
private String encodedVersion;
#PostConstruct
public void setup() {
encodedVersion = new String(Base64.getEncoder().encode(version.getBytes())).replace("=", "");
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**").addResourceLocations("classpath:/static/").setCacheControl(CacheControl.maxAge(365, TimeUnit.DAYS)).resourceChain(true)
.addResolver(new VersionResourceResolver().addContentVersionStrategy("/**"))
.addTransformer(new ResourceTransformer() {
#Override
public Resource transform(HttpServletRequest request, Resource resource, ResourceTransformerChain transformerChain) throws IOException {
// Be aware of side effects changing line break
String result = new BufferedReader(new InputStreamReader(resource.getInputStream())).lines().collect(Collectors.joining("\n"));
result = result.replace("{{cacheVersion}}", encodedVersion);
return new TransformedResource(resource, result.getBytes());
}
});
}
}
pop.js
import mod1 from './mod1.js?cache={{cacheVersion}}';
function dis() {
console.log("hello")
}
Since the version is added as ModelAttribute it will be available in all request mapping. For every version, this will be changed and the way you pull files can be using this cache version variable.

how to add/access a JS compiled from GWT to/in another external html/JS project

I am tasked to compile the gwt project(which doesnot include HTML CSS) into JS files and add the same to an external JS/HTML file( which is in different project).
here is the java code which has to be compiled:
1. Client class:
package com.dell.supportassist.gwt.collectionReport.client;
import org.timepedia.exporter.client.Export;
import org.timepedia.exporter.client.Exportable;
#Export("HelloWorld")
public class HelloWorld implements Exportable {
public String sayHello(){
return "Hello";
}
}
EntryPoint Class:
package com.dell.supportassist.gwt.collectionReport.client;
import org.timepedia.exporter.client.Export;
import org.timepedia.exporter.client.Exportable;
#Export("HelloWorld")
public class HelloWorld implements Exportable {
public String sayHello(){
return "Hello";
}
}
My issue is, once the above gwt project/classes are complied, i want to access 'sayHello()' method in ma external javascript like this:
var person = hello.sayHello();
system.log(person);
But this is throwing a run time error saying 'hello' is not defined.
P.S I am trying to use the GWT compiled JS in an external HTML, JS Present in Durandaljs Framework.
Haven't done this kind of stuff for quite some time but you could achieve this using JSNI, which allows you to write native JS in GWT. From JSNI you can reference your GWT methods. That way you could define a function (on window, most typically) which would then become available to regular JS. From this JSNI-method you can reference your GWT/Java code.
Code example:
public class MyModule implements EntryPoint {
static {
export();
}
/**
* Makes our setData method accessible from plain JS
*/
private static native void export() /*-{
$wnd.setData = #my.package.MyModule::setData(Lcom/google/gwt/core/client/JavaScriptObject;);
}-*/;
private static void setData(JavaScriptObject javaScriptObject) {
// this method is now reachable as window.setData
}
}

Categories