0
votes

Currently working on a project where I am trying my best to avoid any C# in a code block on the razor file so I'm trying to follow the MVVM design pattern. I have a test viewmodel that is just trying to get a string value from the tag markup.

ViewModel

  public class MyViewModel : NotifiableAndDisposableBase
  {
    [Parameter]
    public string Text { get; set; }    
  }

MyCard Component

@inject MyViewModel vm
<h3 style="color: white">MyCard</h3>
<h3 style="color: white">@vm.Text</h3>

View

<div>
  <MyCard Text="This is passed in"/>
</div>

Now, this code will not work the Text parameter will not be recognized in the tag call. The only way I CAN get a parameter to be recognized and passed is if I use a @Code block like so

@code{
   [Parameter]
   public string CodeBlockText {get; set;}
}

OR

Instead of my component using [Inject] my viewmodel it [Inherits] my viewmodel, but using Inherits means I have to use the Default constructor, but I need to use the persistent HttpClient that was created in the StartUp.cs

I figured a work around would be to create a parameter in the code block then set the actual property in the view model, but I'm not sure if that's good practice.

Any suggestions would be appreciated.

2

2 Answers

0
votes

Injection does not work like that. You need to set up the provider in Program.cs.

Something like builder.Services.AddScoped<MyViewModel>();

0
votes

What is a component in the context of MVVM? It's a part of the view. The view should offer you properties where you can bind your ViewModel. So, you don't need any blazer specific declaration in your view model.

MyViewModel

  public class MyViewModel : NotifiableAndDisposableBase
  {
     public string Text { get; set; }    
  }

As an exaggeration, if you have experience with WPF you should be able to reuse the same ViewModel classes, regardless of Blazor of WPF.

Assuming that your component's job is to display your ViewModel, create a Parameter that accepts an instance of the ViewModel. I wouldn't inject the ViewModel in the component because there might be a couple of different ways to create this ViewModel instance. It could be loaded from a service. It could be created when you start editing. So, we create a component if a very particular purpose: Displaying the ViewModel. This component shouldn't care where the instance came from.

MyViewModelDisplayComponent

<h3 style="color: white">MyCard</h3>
<h3 style="color: white">@Model.Text</h3>

@code{
   [Parameter]
   public MyViewModel Model {get; set;}
}

However, when the question of getting an instance is no longer a concern of the component. Who is then in charge? Because your component could be used by other components, like a page, for instance, it is now their responsibility.

MyViewModelDetailPage

@page "/myviewModel/Details/{Id}"
@inject MyViewModel vm

<MyViewModelDisplayComponent Model="vm"  />

@code{
   [Parameter]
   public Guid Id {get; set;}

    protected override async Task OnParametersSetAsync()
    {  
           await vm.LoadDetails(Id);
    }
}

MyViewModel

  public class MyViewModel : NotifiableAndDisposableBase
  {
     private HttpClient _client;

     public MyViewModel(HttpClient client)
     {
         _client = client;
     } 

     public string Text { get; set; }

     public async Task LoadDetails(Guid id)
     {
        Text = await client.GetFromJsonAsync<String>("YOUR_URL",id);
     }
    
  }

If it is wise to inject the ViewModel highly depends on the context, I think. Based on your input, you are using a HttpClient inside your ViewModel. So, the model could load (or send) the data accordingly, as showcased.

However, in the example, you could see that your components are only responsible for translating "Blazor" things, like URL binding to a variable, but not more.

I hope this triggers some thoughts.