I'm working on a Blazor project and I'm trying to move one of the JavaScript files to a class library, I've read the following guides:
-
I'm not sure what I'm missing or what I need to do to get this to work so here is the setup I have:
Facts:
- I'm using
.NET 6
. - I'm using Visual Studio for Mac
Version 17.0 Preview (17.0 build 8754)
. - The name of the solution is
LinkScreen
. - The name of the application project is
Client.Web
. - The name of the application assembly is
LinkScreen.Client.Web
. - The name of the application default namespace is
LinkScreen.Client.Web
. - The hosting model for the application is Web Assembly.
- The name of the class library project is
Client.BrowserInterop
. - The name of the class library assembly is
LinkScreen.Client.BrowserInterop
. - The name of the class library default namespace is
LinkScreen.Client.BrowserInterop
.
Project Structur and Code
- Inside the class library I have a script file called
screen-capture.js
under the following directorywwwroot\scripts
like so:
The build action for this is set to Content and copy to output directory is set to Do not copy.
- The class library is referenced to the application like so:
- I have a C# class called
ScreenCapture
that wraps the JavaScript module that is thescreen-capture.js
and retuns a reference like so:
public static async ValueTask<ScreenCapture> CreateAsync( IJSRuntime jsRuntime, ElementReference videoTagReference) { var jsModuleRef = await jsRuntime.InvokeAsync<IJSInProcessObjectReference>("import", "./_content/LinkScreen.Client.BrowserInterop/screen-capture.js").ConfigureAwait(false); return new ScreenCapture(jsModuleRef, videoTagReference); }
Previously I had a reference to the
screen-capture.js
insideindex.html
like so<script src="_content/LinkScreen.Client.BrowserInterop/scripts/screen-capture.js" type="module"></script>
because I thought it was required to use theIJSRuntime
methods and then someone on the Blazor channel enlighten me so I removed the reference but I'm still getting the same 404 error now from the wrapper by callingjsRuntime.InvokeAsync
.It's important to note that everything works correctly when the script is inside the
wwwroot/scripts
of application folder.- My program.cs file is the default for .NET 6 Blazor WebAssembly projects and is like so:
using Microsoft.AspNetCore.Components.Web; using Microsoft.AspNetCore.Components.WebAssembly.Hosting; using LinkScreen.Client.Web; var builder = WebAssemblyHostBuilder.CreateDefault(args); builder.RootComponents.Add<App>("#app"); builder.RootComponents.Add<HeadOutlet>("head::after"); builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); await builder.Build().RunAsync();
Things I've tried and it still doesn't work:
- Clean the project, delete the bin and obj directories.
- Clear the browser's cache as well as disabling it.
- I've tried both Safari and Chrome but I get the exact same issue.
CodePudding user response:
Are you getting a 404 error? try to modify your script source
<script src="_content/LinkScreen.Client.BrowserInterop/scripts/screen-capture.js" type="module"></script>
to
<script src="./_content/Client.BrowserInterop/scripts/screen-capture.js" type="module"></script>
as the offcial document explains:
The path segment for the current directory (./) is required in order to create the correct static asset path to the JS file. The {PACKAGE ID} placeholder is the library's package ID. The package ID defaults to the project's assembly name if isn't specified in the project file.
in my case ,it could work afer I modified the source:
CodePudding user response:
I was just trying to export my own components to a library and re-discovered the most important Blazor WASM rule :
- Always clear the Client bin and obj folders.
What worked after copying the folders from the main project to the class library failed after a couple of executions. It seems the
bin
folder still contained some scripts so the paths that worked in the main project kept working until a full clean removed them.If the script is stored in
wwwroot/scripts
and you use :<script src="./_content/Client.BrowserInterop/scripts/screen-capture.js
Your methods should be available globally.
You should consider using JS isolation with side-by-side component scripts to avoid polluting the JS namespace and cluttering your
wwwroot
folder.If your component is names
ScreenCapture.razor
, create a JS module in the same folder namedScreenCapture.razor.js
. You can load this as a module in your component'sOnAfterRenderAsync
and call its exported methods :private IJSObjectReference? module; protected override async Task OnAfterRenderAsync(bool firstRender) { if(module ==null) { var path = "./_content/Client.BrowserInterop/ScreenCapture.razor.js"; module = await JS.InvokeAsync<IJSObjectReference>("import", path); } } private ValueTask SetTextAsync(string text) { if (module is null) { return ValueTask.CompletedTask; } return module.InvokeVoidAsync("setValue", TextBox, text); }
Example
I was trying to put the code from this TagSelector component into my own RCL library using JS isolation to keep things tidy. The CSS and JS files are stored in
wwwroot
directly so normally I'd have to use :<link rel="stylesheet" href="_content/MW.Blazor.TagSelector/styles.css" /> <script src="_content/MW.Blazor.TagSelector/interop.js"></script>
By renaming the files to
TagSelector.razor.css
andTagSelector.razor.js
though, I was able to keep them together with the Razor file. I only need to use theTagSelector
component now. The CSS and JS files are imported automatically.The JS file was changed to a module :
export function getValue(element) { return element.value; } export function setValue(element, value) { element.value = value; } export function blur(element) { element.blur(); }
The module is loaded with :
var path = "./_content/MyLibraryName/TagSelector.razor.js"; module = await JS.InvokeAsync<IJSObjectReference>("import", path);
To call the exported
setText
method, the following wrapper is used:private ValueTask SetTextAsync(string text) { if (module is null) { return ValueTask.CompletedTask; } return module.InvokeVoidAsync("setValue", TextBox, text); }
- I'm using