misanthrop
misanthrop

Reputation: 939

How to pass ViewModels into Razor Components in .NET Core 3.1

I have a View MyView.cshtml with the following content:

@using MyProject.ViewModels
@model MyProject.ViewModels.MyViewViewModel

<form asp-action="Test" method="Post">
    <component type="typeof(MyProject.Views.Home.Test)" render-mode="ServerPrerendered" />
    <input type="submit" value="send"/>
</form>

And I have the Razor Component Test.razor with the following content (with Blazor Syntax):

@page "/Test"

<div class="form-group top-buffer @Visible">
    <div class="row">
        <div class="col-2">
            <label asp-for="TestName" class="control-label"></label>
        </div>
        <div class="col-3">
            <input asp-for="TestName" class="form-control" />
            <span asp-validation-for="TestName" class="text-danger"></span>
        </div>
    </div>
</div>

<button @onclick="Show">Show</button>

@code {
    public string Visible { get; set; } = "hidden";

    protected async Task Show()
    {
         Visible = "";
    }
}

The Class MyViewViewModel would look like this:

namespace MyProject.ViewModels
{
    public class MyViewViewModel
    {
        [Display(Name = "Test Name:")]
        public string TestName { get; set; }
    }
}

Works all pretty fine so far. However I now want to use this component as part of a Web form which will be sent to the controller after submission. That's why I need to access and change properties of my ViewModel 'MyViewViewModel'. Unfortunately I did not find any answer in the internet on how to do that. I can't use @model MyProject.ViewModels.MyViewViewModel like in the view because this will give me a compilation error. I wonder if I need to use @inject, but if yes, I don't know how...

(parts are used from this example: https://jonhilton.net/use-blazor-in-existing-app/)

Upvotes: 1

Views: 5205

Answers (2)

Brett Manners
Brett Manners

Reputation: 320

To hopefully add to the info. With a ASP.Net Core MVC project host Blazor webassembly, I was trying to pass a viewmodel into a razor component using this code in my view cshtml file: <component Type="typeof(Leave)" render-mode="WebAssembly" model="new { model = (MyViewModel)@Model})"/>

But it would fail to render the razor component if I tried to access data in the viewmodel from the razor component with an Object not set exception. I think it was accessing the data before the view model has been initialized. Maybe if I set a default value this could avoided?

I found by using this instead I was able to get it working.

@(await Html.RenderComponentAsync<Leave>(RenderMode.WebAssembly,new { model = (MyViewModel)@Model}))

Edit Seems you also need to register the viewModel class in the services in the Blazor WASM project in the Program.cs file.

builder.Services.AddScoped(sp => new HttpClient {BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
builder.Services.AddScoped<MyViewModel,MyViewModel>();   // <= add this line

await builder.Build().RunAsync();`

Without that I would get an error saying the property could not be found.

Hopefully this saves someone else some time :-)

Upvotes: 1

Ed Charbeneau
Ed Charbeneau

Reputation: 4634

When you mix Blazor in a Razor Page, you can do the following:

  • Render a Razor Component
  • Interact with a Razor Component
  • Pass a Razor Component values

Please keep in mind that you are dealing with two different life-cycles. So if you do work inside of a Razor Component, the component will update but not effect the Razor Page it is hosted inside of. So mixing Razor Components and Pages with forms would be difficult.

More specifically to the OP. To pass data from your ViewModel to the component you may use the following method.

@using MyProject.ViewModels
@model MyProject.ViewModels.MyViewViewModel

<form asp-action="Test" method="Post">
    <component type="typeof(MyProject.Views.Home.Test)" 
               render-mode="ServerPrerendered" 
               param-Name="@Model.TestName"/>
    <input type="submit" value="send"/>
</form>

Test.razor

<h3>HelloWorld</h3>

Hello @Name

@code {

    [Parameter]
    public string Name { get; set; } = "undefined";

}

About life cycles Basically when you have a button in Blazor, it will trigger an event which causes the component to re-render. You could imagine it like an iframe, or update-panel. When you have a button in a Razor page, it does a HTTP call round trip and reloads the page entirely. There is no event system in place to tell Blazor to invoke an HTTP call round trip to refresh the Razor page's content and vise versa. You can only one-way data-bind from Razor pages to Blazor, think write-only, and only when the page loads.

Upvotes: 4

Related Questions