Jacob Dachenhaus
Jacob Dachenhaus

Reputation: 91

Blazor: Creating a form using partial components

Question

I have a class:

// Person.cs
public class Person
{
  public string Name { get; set; }
  public DateTime DateOfBirth { get; set; }
  public double Temperature { get; set; }
  public double Pulse { get; set; }
  public double Weight { get; set; }
}

I want to create a form that reuses partial forms as components, such as this:

// MyForm.razor
<h1>Currently editing: @Person.Name</h1>
<EditForm EditContext="EditContext">
  <PersonDefault />
  <PersonHealthStats />
</EditContext>
@code {
  private Person Person;
  private EditContext? EditContext;
  protected override void OnInitialized()
  {
    EditContext = new(Person);
  }
}

How would I go about creating these partial components?

Requirements

  1. The PersonDefault component should include inputs for the Name and DateOfBirth fields.
  2. The PersonHealthStats component should include inputs for the Temperature, Pulse, and Weight fields.
  3. When a field is updated, it should update Person
  4. When the Name field is updated, it should sync with the h1 tag.

What I've Tried

1. Binding Person to the child component

// MyForm.razor
<PersonDefault @bind-Person="Person" />

// PersonDefault.razor
<input @bind="Person.Name" />
@code {
  [Parameter] public Person Person { get; set; }
  [Parameter] public EventCallback<Person> PersonChanged { get; set; }
}

This solution works for updating the values on Person, but doesn't sync with the h1 tag.

2. Binding individual parameters for each field

// MyForm.razor
<PersonDefault @bind-Name="Person.Name" />

// PersonDefault.razor
<input @bind="Name" />
@code {
  [Parameter] public EventCallback<string> NameChanged { get; set; }
  private string _Name;

  [Parameter] public string Name {
    get => _Name;
    set
    {
      if (_Name == value) return;
      _Name = value;
      NameChanged.InvokeAsync(value);
    }
  }
}

This solution works for updating the values on Person and syncing the h1 tag, but creates a lot of redundant code the more input fields I add to the component.

EDIT:

I made an image to better illustrate what I'm trying to achieve

Image

Upvotes: 1

Views: 741

Answers (1)

Jacob Dachenhaus
Jacob Dachenhaus

Reputation: 91

I decided to revisit solution 1 and was able to get the sync working. Here's my code:

<InputText @bind-Value="Name" />

@code {
  [Parameter] public Person Person { get; set; }
  [Parameter] public EventCallback<Person> PersonChanged { get; set; }

  private Person _Person; // Backing store

  private string Name
  {
    get => _Person.Name;
    set
    {
      if (_Person.Name == value) return;
      _Person.Name = value;
      InvokePersonChanged();
    }
  }

  protected override void OnInitialized()
  {
    _Person = new() {
      Name = Person.Name
    };
  }

  private void InvokePersonChanged()
  {
    if (PersonChanged.HasDelegate) PersonChanged.InvokeAsync(_Person);
  }
}

Upvotes: 3

Related Questions