dotnet / aspnetcore

ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.
https://asp.net
MIT License
35.37k stars 9.99k forks source link

Static files not working on Blazor apps using Razor Class Library (RCL) #13279

Closed arivoir closed 5 years ago

arivoir commented 5 years ago

Describe the bug

Static resources in the "wwwroot" folder of a Razor class library RCL, aren't deployed correctly. This is causing apps to crash due to the JavaScript interop to throw exception because js files aren't in place.

To Reproduce

Steps to reproduce the behavior:

  1. Create a Blazor application (Any server-side or client-side)
  2. Create a new RCL project.
  3. Add a reference from the app to the created library
  4. Add the tag to Index.razor.
  5. Run the app.

Expected behavior

It should render the component with the "background.png" included in the css style of the library.

Screenshots

image The background of the component doesn't appear.

Additional context

Using the latest preview 8

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.Components" Version="3.0.0-preview8.19405.7" />
    <PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="3.0.0-preview8.19405.7" />
  </ItemGroup>
pranavkm commented 5 years ago

@arivoir did you include the css and scripts from the Razor Class Library in your main application? These aren't auto included in your Blazor application.

arivoir commented 5 years ago

These aren't auto included in your Blazor application.

From the documentation https://docs.microsoft.com/en-us/aspnet/core/blazor/class-libraries?view=aspnetcore-3.0&tabs=visual-studio

An RCL can include static assets. The static assets are available to any app that consumes the library. For more information, see Reusable Razor UI in class libraries with ASP.NET Core.

And the other documentation page https://docs.microsoft.com/en-us/aspnet/core/razor-pages/ui-class?view=aspnetcore-3.0&tabs=visual-studio

When packing an RCL, all companion assets in the wwwroot folder are included in the package automatically and are made available to apps referencing the package.

Do you mean this isn't true?

did you include the css and scripts from the Razor Class Library in your main application?

Yes, I tried copying the files manually in the app and adding the references in _Host.cshtml. It works in server-side Blazor, but not in client-side Blazor.

nikfp commented 5 years ago

I can second this issue, following Microsoft documentation does not provide a successful path to functionality for the RCL.

Steps taken to reproduce and result are the same as @arivoir outlined above.

danroth27 commented 5 years ago

The static assets are made available to the consuming app, but you still need to reference them to bring them in. See https://docs.microsoft.com/aspnet/core/razor-pages/ui-class?view=aspnetcore-3.0#consume-content-from-a-referenced-rcl.

If you think the docs could still use some improvement, we'd be happy to accept a PR with a proposed revision.

arivoir commented 5 years ago

but you still need to reference them to bring them in

What do you exactly mean? I actually have a project-reference to the library from the app.

danroth27 commented 5 years ago

@arivoir A project reference is no longer sufficient. You also need to add script tags, link tags, etc for the static asset. For example:

<script src="_content/MyLibrary/foo.js"></script>
arivoir commented 5 years ago

@danroth27 Good point. I had missed some of them.

Server-side Blazor is working fine, the problem is with client-side. The css files are available, but the js not.

Server-side image

Client-side image

It´s intriguing for me why in server-side Blazor, the resources are in a folder named "_content", whereas in client-side the folder is named "~/_content"

pranavkm commented 5 years ago

According to @javiercn this is limited to standalone client-side apps.

michaeldaw commented 5 years ago

@danroth27 I just have a question about your comment:

A project reference is no longer sufficient. You also need to add script tags, link tags, etc for the static asset.

How will this affect the situation where a package is distributed by Nuget? How will the end-user know to include script/link tags for static files in their index.html file?

danroth27 commented 5 years ago

How will this affect the situation where a package is distributed by Nuget? How will the end-user know to include script/link tags for static files in their index.html file?

NuGet packages can still carry the static assets and make them available to the app, but the consumer of the package will need to add the script/link tags manually. The docs for the NuGet package will need to make this clear.

michaeldaw commented 5 years ago

@danroth27 Thank you for explaining. I'm sure there's a lot going on behind the scenes that I don't understand, but I'm curious about the reasoning for this change. It used to "just work" automatically. Now we have to undertake manual steps to include these static files.

danroth27 commented 5 years ago

One of the main reasons for this change is to enable the consumer of the package to control the order in which the scripts are added, as the order is functionally significant.

arivoir commented 5 years ago

@danroth27 Don't you think importing and using nuget packages is more important than controlling the order in which the scripts are added? The first looks like a very important scenario whereas the second is a border case to me. At least, the default project template should come with a @(ImportStaticResources()), where advanced users could remove it and perform all the importing's manually. What do you think?

javiercn commented 5 years ago

@arivoir There are many challenges that arise from importing resources automatically. Importing assets automatically is a very complicated problem that has to deal with same of the similar concerns that package managers have to.

For an automatic "import system" you need to take into account:

This is a non trivial problem to solve and we chose not to try and solve it at this point; it is also not likely we try to solve it in the future either. This is also something that is also additive. All the information regarding the static web assets for the app is available at build time and nothing prevents anyone from using that information to create a partial view or set of partial views that automatically import the assets. That said, if you do that then you will have to handle all the concerns above (and possibly more).

Our approach is to simply make all the assets available to you at build and publish time and to let you choose what content to bring into your app.

@michaeldaw I hope this also answers your question. We experimented with the automatic imports in the past, but it turns out its really complicated to get right on the general case, so when we productionized the feature we decided to scope it out.

@arivoir Are you still experiencing any issue here? If so please reopen this issue and provide the following info:

arivoir commented 5 years ago

@javiercn It's a pity the automatic importing of the resources can not be done. This is a very serious usability problem.

Did you see https://github.com/aspnet/AspNetCore/issues/13279#issuecomment-524579014 @danroth27 confirmed it is a bug. Is it fixed?

javiercn commented 5 years ago
  • Is the app a standalone blazor wasm app? (This is a known issue)

We are not aware of any other issue in any other scenario. If you are still experiencing this, can you provide a minimal repro github repo we can clone to further investigate the issue?

arivoir commented 5 years ago

Do you say the issue was fixed? If it was I have nothing else. it was only that one.

javiercn commented 5 years ago

@arivoir I think so. Please give it a try with the latest preview and file an issue with a repro github repro so that we can investigate if you find out is not the case.

The only known issue we haven't fixed yet is Blazor standalone, but in any other scenario there should be no issue. (Blazor hosted or server-side blazor).

limefrogyank commented 5 years ago

Not sure if this is going to not work in the future, but adding in the following to your component library project will bring back the old behavior (client-side js and css files are automatically added to your html page).

<ItemGroup>
    <!-- .js/.css files will be referenced via <script>/<link> tags; other content files will just be included in the app's 'dist' directory without any tags referencing them -->
    <EmbeddedResource Include="wwwroot\**\*.js" LogicalName="blazor:js:%(RecursiveDir)%(Filename)%(Extension)" />
    <EmbeddedResource Include="wwwroot\**\*.css" LogicalName="blazor:css:%(RecursiveDir)%(Filename)%(Extension)" />
    <EmbeddedResource Include="wwwroot\**" Exclude="**\*.js;**\*.css" LogicalName="blazor:file:%(RecursiveDir)%(Filename)%(Extension)" />
  </ItemGroup>
michaeldaw commented 5 years ago

@limefrogyank Thanks for mentioning this. I've been meaning to ask if this functionality will be removed. As it stands, "old" projects made before this capability was removed still work (as long as they retain the code you posted). @danroth27 Will this continue to work in the future? I think it could still be useful for internal-use-only projects.

javiercn commented 5 years ago

@limefrogyank @michaeldaw Using embedded files is going away. We didn't have time to remove it from blazor client-side, but it was an experimental feature. The production version of the feature is static web assets.

dazinator commented 5 years ago

@javiercn Quick question - You mentioned embedded assets. Was this a way to embed js, css files in the assembly that is downloaded to the client, such that, script / link tags would resolve the embedded script / Css file from the assembly directly in the browser without having to make another request to the server - because if so, that feature seems very valuable to save roundtrips with the server and to hear that it would be disappearing makes me very sad!

javiercn commented 5 years ago

@dazinator It has a lot of drawbacks actually and there are better alternatives without those drawbacks. The number of requests doesn't matter much in HTTP/2, if you don't bundle the resources in the assembly you can use many web optimization techniques and let the browser decide the priorities for what to download first.

By not being embedded, you get to decide what resources to download and in which order (for example putting things on the head tag). If you want to reduce the amount of requests you can bundle and minify your assets, etc.

Embedding the resources seems convenient, but its very limiting and doesn't handle very common cases, like script loading order, etc.

dazinator commented 5 years ago

@javiercn

let the browser decide the priorities for what to download first.

In terms of css / js are you referring to ES6 module / imports to steer the browser in this regard? Blazor applications dont tend to have a lot of modular js (otherwise it would be a js spa) but modular css (imports) is obviously pretty standard. Quite often that would be bundled / minified for production (even though http 2 it's still common to bundle imho)

By not being embedded, you get to decide what resources to download and in which order (for example putting things on the head tag)

If files were embedded, could I not still decide which embedded files to load and in which order (I believe I could / should still be able to dictate that based on the script and link tags I put in my page that resolve to embedded files - so not sure I understand your point here)

If you want to reduce the amount of requests you can bundle and minify your assets, etc.

.. yep and if I embed those bundled and minified assets into my blazor wasm application assembly, I could link these in my index.html without the browser having to request them from the server. This approach also solves a problem of any version mismatch between the application version and asset version. For example if assets are not embedded, then the correct version of the asset must be requested from the server for the application. The browser may then cache that asset - which means worrying about how to invalidate that when we deploy a new version of the application. There are of course common schemes to handle this, but with the embedded assets approach this problem goes away as the only thing you have to version is the application itself.

Basically I understand what you are saying, and I am not making the case that blazor should solve the issue of automatically adding script and links to my page or solving load order or anything like that. Only arguing for the ability to access embedded files (whatever they may be) and optionally include some embedded files as link or scripts in a page (where I control the placement and thus load order).

danielcrenna commented 5 years ago

@arivoir this works if you add UseStaticWebAssets in your WebHostBuilder configuration. I ran into the same problem, but adding that makes the static files in the RCL available via _content/...

michaeldaw commented 5 years ago

@javiercn This change seems to have affected the dotnet publish command when run against a blazor app.

Static content from "Old-style" Razor Class Libraries with the EmbeddedResource statements and static assets in the content folder appear in the folder [publish folder]/[project name]dist/_content folder as expected when using the dotnet publish command.

However, static content from "New-style" Razor Class Libraries without the EmbeddedResource statements and assets in the wwwroot folder appear in the folder [publish folder]/wwwroot.

I noticed this when attempting to publish my app to an Azure Web App. Using the typical process, the dist folder (containing index.html, etc.) is published to the web app, but the resources in [publish folder]/wwwroot are missing.

I realize this explanation is somewhat convoluted, but I believe the same issue was brought up in #13103 and hopefully that will help make the issue clearer.

limefrogyank commented 5 years ago

I made a really crude VSIX extension that will list out all of the css and js assets from nuget packages of razor class libraries you've added. You can then check off which ones you want to include and generate the Githubissues.

  • Githubissues is a development platform for aggregating issues.