Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Adding Server-Side Blazor to an existing MVC Core app

I have a fully operative Blazor server-side project and a .NET Core 3 MVC project, both in the same solution. I'm now trying to use the components from the Blazor project in my MVC project.

I've managed to get the components to render on an MVC page, but button clicks on the Blazor component don't trigger the onclick events for the components, instead I just get navigated to https://localhost:44341/?

I'm unclear as to exactly what changes I need to make in the MVC solution to get this working? The official documentation talks about using components in and MVC project and it's mentioned in this video chat with Dan Roth, but neither go into the detail of how to achieve it.

Could anyone walk me though the full steps on how to retrofit server-side Blazor into an MVC project (or point me at an example)?

What I've tried

My Blazor project still has the standard template's Counter component, so I've tried to add that to my MVC solution by referencing the Blazor project and then adding this to my index.cshtml file:

<div id="Counter">
  @(await Html.RenderComponentAsync<Counter>())
</div>

That renders correctly but the button clicks don't work.

I've added the JS file to MVC apps _Layout page:

<script src="_framework/blazor.server.js"></script>

Turned on Server Side Blazor in the MVC project's Startup.cs file:

services.AddServerSideBlazor();

I've also added Pages and Shared directories to the MVC project and copied over the _Imports and MainLayout files - not sure if that's needed.

When that didn't do it, I tried spinning up new projects from all the Blazor templates (client, client-hosted and server-side) to look for clues, but all of those examples are set up as complete SPAs with a single Blazor entry point (the App file) hosted in a .html file, whereas I want to render Blazor components on lots of different existing MVC pages.

I then experimented with adding parts of those projects (and parts of my working Blazor project) into the MVC project, but can't get it to work fully.

I tried following the answer here too, but that's working with adding the Blazor component files into the MVC project, whereas my Blazor files are currently (mostly) in a different project.

Random questions from my experiments to date

  • I've added services.AddServerSideBlazor(); in the MVC app's Startup.cs. is anything else needed there?
  • My MVC app is still on traditional AspNetCore.Routing, do I still need to switch to Core 3's endpoint routing if I'm only placing the components on existing MVC pages (I.e. I won't have Blazor pages, just Blazor components).
  • Does the server-side part of Blazor that the blazor javascript file running in the browser talks to (via SignalR) need endpoint routing? I'm getting a 404 in the browser from the js file.
  • Do I still need the `App' (App.razor) class/page as an entry point if I'm just hosting components?
  • If so, do I need to put the <app>@(await Html.RenderComponentAsync<App>())</app> line from _Host.cshtml somewhere in my MVC project?
  • Do I need to reproduce the Pages and Shared folders from the Blazor project in my MVC project, and also the two _Imports.razor files and the MainLayout.razor file?
  • I think Blazor is now part of the Microsoft.NETCore.Platforms file (referenced under the SDK node in the MVC project), so am I right to think I don't need to add separate NuGet packages for Blazor to the MVC project?
like image 717
tomRedox Avatar asked May 21 '19 14:05

tomRedox


People also ask

Does Blazor replace MVC?

Blazor is an alternative to MVC and Razor Pages but with a twist: It's a single-page app framework (SPA) that just happens to use C# instead of JavaScript. Blazor applications can run on the server, or in the browser thanks to Web Assembly (https://www.telerik.com/blogs/goodbye-javascript-hello-webassembly).

Can you mix Blazor and Razor?

Razor components can be integrated into Razor Pages and MVC apps in a hosted Blazor WebAssembly solution.


1 Answers

I've got to the bottom of this now. After all the trial and error described in my question the solution turns out to involve surprisingly little code.

Example With Server-Side Blazor And MVC In The Different Projects In The Same Solution

The answer below assumes you have a working server-side Blazor project in your solution and are trying to use components from that project in a separate MVC project in that same solution. I did this in VS2019, using Core 3 Preview 5.

See the bottom of the page or a link to an example of MVC and Blazor in the same project.

Changes to the MVC project's .csproj file

You just need to add a reference to your blazor project:

    <ProjectReference Include="..\MyClient.Web.Blazor\MyClient.Web.Blazor.csproj" />

Changes to the MVC project's Views\Shared\_Layout.cshtml file

Add the base url into the head:

<head>
<meta charset="utf-8" />
<base href="~/" />

Add a reference to the Blazor JS Script

<script src="_framework/blazor.server.js"></script>

(This doesn't actually exist on your local file system, Blazor automagically sorts it out by the time the app gets to the browser)

Changes to the MVC project's Startup.cs

This is where the real work is done

In the ConfigureServices method add:

services.AddServerSideBlazor();

I then switched my configuration over to the new style Core 3 configuration (so no longer using the AddMvc method):

services.AddControllersWithViews()            
    .AddNewtonsoftJson(options =>
    {
        options.SerializerSettings.ContractResolver = new DefaultContractResolver();
    });

services.AddRazorPages();

And then the change that finally got it all working for me was to switch to Core 3's endpoint routing in the Configure(IApplicationBuilder app, IWebHostEnvironment env) method:


app.UseRouting();

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllerRoute(
        name: "default",
        pattern: "{controller=Home}/{action=Index}/{id?}");
    endpoints.MapRazorPages();
    endpoints.MapBlazorHub();
});

//app.UseMvc(routes =>
//{
//    routes.MapRoute(
//        name: "MyArea",
//        template: "{area:exists}/{controller=Home}/{action=Index}/{id?}");

//    routes.MapRoute(
//        name: "default",
//        template: "{controller=Home}/{action=Index}/{id?}");
//});

The commented out code shows the old style routing I was using. I think it was the absence of endpoints.MapBlazorHub(); that was causing my button click issue

Changes to the Blazor project

No changes were needed to the Blazor project to get this working.

How to then put the Blazor component on an MVC page

Once you've done all of the above, all that's needed to get your component to render on an MVC page is to add

 @(await Html.RenderComponentAsync<YourComponent>())

That should work on both an MVC page and a Razor page.

Known routing issue in Core 3 Preview 5

The above changes were all that I needed to make to get it working. There is a known issue with Core 5 where once you navigate to an MVC or Razor page with a Blazor component on it, the routing will no longer work until the page is refreshed - the URLs will change in the browser's address bar, but no navigation will occur.

I'm now running on Preview 6 and I can confirm the routing issue is now fixed from Preview 6 onward.

Example With Server-Side Blazor And MVC In The Same Project

Chris Sainty has an example of server-side Blazor components added in to an existing MVC project here: Using Blazor Components In An Existing MVC Application (source here)

like image 61
tomRedox Avatar answered Oct 16 '22 11:10

tomRedox