Reputation: 2339
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 getting the following error:
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:
.NET 6
.Version 17.0 Preview (17.0 build 8754)
.LinkScreen
.Client.Web
.LinkScreen.Client.Web
.LinkScreen.Client.Web
.Client.BrowserInterop
.LinkScreen.Client.BrowserInterop
.LinkScreen.Client.BrowserInterop
.Project Structur and Code
screen-capture.js
under the following directory wwwroot\scripts
like so:The build action for this is set to Content and copy to output directory is set to Do not copy.
ScreenCapture
that wraps the JavaScript module that is the screen-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
inside index.html
like so <script src="_content/LinkScreen.Client.BrowserInterop/scripts/screen-capture.js" type="module"></script>
because I thought it was required to use the IJSRuntime
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 calling jsRuntime.InvokeAsync
.
It's important to note that everything works correctly when the script is inside the wwwroot/scripts
of application folder.
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:
Upvotes: 4
Views: 7301
Reputation: 2080
I faced the same problem and I found the issue was different - CORS. I installed a plugin to enable CORS and it worked. Unfortunately the CORS error always showed up after showing the javascript (module not found/undefined) making it harder to establish the cause.
Upvotes: 0
Reputation: 2339
Eventually I solved this by following the steps below:
Now, I think that the reason the component as a library didn't work for my old project are as follows:
I noticed that the Server didn't serve the Client and the Client was the project that was setup as the Startup project as opposed to the new project that I made.
I used a regular Class Library and not RCL in the project because I didn't think that there is a difference except the starter files but apparently there is more to it.
Upvotes: 1
Reputation: 4298
Have you tried adding builder.WebHost.UseStaticWebAssets();
to your Program.cs
of the Host Net Core app?
Upvotes: 1
Reputation: 131712
I was just trying to export my own components to a library and re-discovered the most important Blazor WASM rule :
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 named ScreenCapture.razor.js
. You can load this as a module in your component's OnAfterRenderAsync
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
and TagSelector.razor.js
though, I was able to keep them together with the Razor file. I only need to use the TagSelector
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);
}
Upvotes: 8
Reputation: 11924
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:
Upvotes: 0