'How do I make inheritance work from component class?

Im trying to access Ids from a component class in an If statement but I get the error saying that it does not exist in the current context (CS0103).

Here is my code:

    @inherits MainViewModel

<button>Delete</button>
<button>Add</button>
<br />
<label>Vehicles</label>

<EditForm Model="@VehicleFuel">
    <InputSelect Id="VehicleList" class="form-group" @bind-Value="@Id">
        <option value="0">Select a vehicle</option>
        @foreach (var item in VehicleFuel)
        {
            <option value="@item.VehicleId">@item.VehicleFuel</option>
        }

        @if (@VehicleId = 1)
        {
        <div>
            <p>Manufacturer</p>
            <input placeholder="Tesla" />
        </div>

        <div>
            <p>Model</p>
            <input placeholder="Model 3" />
        </div>
        } 
    </InputSelect>
</EditForm>

And here is my component class:

using Microsoft.AspNetCore.Components;
using System.Collections.Generic;
using System.Threading.Tasks;
using VehicleSale.Models;

namespace VehicleSale.Components;

    public class MainViewModel : ComponentBase
    {
        public List<VehicleTypes> VehicleFuel  { get; set; }
        public string Id {get; set; } = "0";

        protected override Task OnInitializedAsync()
        {
            VehicleFuel = new List<VehicleTypes>()
            {
                new VehicleTypes{VehicleId=1, VehicleFuel="Electric"},
                new VehicleTypes{VehicleId=2, VehicleFuel="Gas"},
                new VehicleTypes{VehicleId=3, VehicleFuel="Diesel"}
            };
            return base.OnInitializedAsync();
        }

I'm confused where I need to add the component class again so I can use it in my if statement. My end goal is that when either the VehicleID or VehicleFuel is selected, the following input box appear which would be the Manufacturer and Model. I've tried writing @VehicleFuel and I get the same error.



Solution 1:[1]

You should bind using the same type int to int. Also your binding syntax was wrong. Your if statement was an assigment use == or is.

@inherits MainViewModel

<button>Delete</button>
<button>Add</button>
<br />
<label>Vehicles</label>

<EditForm [email protected]>
    <InputSelect Id="VehicleList" class="form-group" @bind-Value="@someModel.VehicleId">
        <option value=0>Select a vehicle</option>
        @foreach (var item in VehicleFuel)
        {
            <option value="@item.VehicleId">@item.VehicleFuel</option>
        }
    </InputSelect>
</EditForm>
@if (this.someModel.VehicleId is 1)
{
    <div>
        <p>Manufacturer</p>
        <input placeholder="Tesla" />
    </div>

    <div>
        <p>Model</p>
        <input placeholder="Model 3" />
    </div>
}
public class MainViewModel : ComponentBase
{
    protected SomeModel someModel  = new();
    protected List<VehicleTypes> VehicleFuel { get; set; }

    protected override Task OnInitializedAsync()
    {
        VehicleFuel = new List<VehicleTypes>()
        {
            new VehicleTypes{VehicleId=1, VehicleFuel="Electric"},
            new VehicleTypes{VehicleId=2, VehicleFuel="Gas"},
            new VehicleTypes{VehicleId=3, VehicleFuel="Diesel"}
        };    
    }
}

public class SomeModel
{
    public int VehicleId { get; set; } = 0;
}

public class VehicleTypes
{
    public int VehicleId { get; set; }
    public string VehicleFuel { get; set; }
}

Solution 2:[2]

I assume you implement inheritance so you can use the VehicleFuel in several children.

There's a different "Clean Design" approach you can take to this by separating your data from your UI.

Define a DI service that holds your Vehicle Fuel data. It can be as simple as creating a list internally or may gett the list from a data store such as a Db or an API service.

First here's a couple of data classes.

public record VehicleType
{
    public int VehicleId { get; init; }
    public string VehicleFuel { get; init; } = "Diesel";
}

public class Vehicle
{
    public int VehicleTypeId { get; set; }
}

And the FuelService:

public class FuelService
{
    public List<VehicleType> VehicleFuel { get; private set }

    public FuelService()
    {
        // define it in code but could get it from a DB
        VehicleFuel =new List<VehicleType>()
        {
                new VehicleType{VehicleId=1, VehicleFuel="Electric"},
                new VehicleType{VehicleId=2, VehicleFuel="Gas"},
                new VehicleType{ VehicleId = 3, VehicleFuel = "Diesel" }
        };
    }
}

Register it in Program.cs. I'm using a singleton as it's the same dataset for all users.

builder.Services.AddSingleton<FuelService>();

We can now Inject the Service into any component that needs to use it.

@page "/"
@inject FuelService FuelService

<EditForm Model="@model">
    <InputSelect Id="VehicleList" class="form-group" @bind-Value="@model.VehicleTypeId" @oninput=UpdateUI>
        <option value="0">Select a vehicle</option>
        @foreach (var item in this.FuelService.VehicleFuel)
        {
            <option value="@item.VehicleId">@item.VehicleFuel</option>
        }

    </InputSelect>
</EditForm>
<div class="p-2">
@if (@model?.VehicleTypeId == 1)
{
    <div>
        <span style="display:block;">Manufacturer</span>
        <input placeholder="Tesla" />
    </div>

    <div>
        <span style="display:block;">Model</span>
        <input placeholder="Model 3" />
    </div>
}
</div>
@code {

    private Vehicle model = new Vehicle();

    private void UpdateUI()
    =>  this.InvokeAsync(StateHasChanged);
}

Some points:

  1. I've moved the display of the Type outside the Select.
  2. I'm triggering a UI Update whenever the value changes using @ininput. You can't use @onchange as the bind process uses that event.
  3. In real life model would also move into a Vehicle service which would have the CRUD methods to fetch from and persist the data to the relevant data store.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2