Hopefully someone can advise me how to get out of this corner I've coded myself into! I'm pretty new to Blazor and have never created a dynamically generated form before. I THOUGHT I was on the right track, but maybe not. I need to add as many of the child component (DynamicStep.razor) to an editform as the user requires. I can add them to the parent page without issue. I can also get the values from the controls...the problem is I just don't have clue which control each of the values came from.

If I'm on the wrong path, can someone point me in the right direction? Any help would be enormously appreciated!

DynamicStep.razor (Child Component):

<div class="col-lg-3">
    <label for="@Id" class="fw-bold">@Id</label>
    <div class="input-group">
        <select id="@Id" @bind:get="StepValue" @bind:set="SetValue!">
            <option selected value="0">N/A</option>
            <option value="1">Pass</option>
            <option value="2">Fail</option>
            <option value="3">In Progress</option>
        </select>
    </div>
</div>

@code {
    [Parameter] public string? Id { get; set; }
    [Parameter] public EventCallback<string> HandleNewStepEvent { get; set; }
    [Parameter] public string? StepValue { get; set; }
    private async Task SetValue(string newValue)
    {
        if (StepValue != newValue)
        {
            StepValue = newValue;
            await HandleNewStepEvent.InvokeAsync(newValue);
        }
    }
}

Parent:

<div class="row">
    <div class="col-lg-12">
        <EditForm Model="@addJobStep" OnValidSubmit="AddJobStepControl" FormName="frmAddJobStep">
            <DataAnnotationsValidator />
            <div class="container-fluid border border-1 rounded p-3" style="background-color:lightgray;">
                <div class="row">
                    <div class="col-lg-3">
                        <label for="stepOrder">Step Order</label>
                        <InputSelect id="stepName" @bind-Value="addJobStep.StepName" class="form-control form-control-sm">
                            <option selected disabled value="">Select a Step</option>
                            @if (stepTitles != null)
                            {
                                @foreach (var val in stepTitles!)
                                {
                                    <option value="@val.Title">@val.Title</option>
                                }
                            }
                        </InputSelect>
                        <ValidationMessage For="@(() => addJobStep.StepName)" />
                    </div>
                    <div class="col-lg-3">
                        <label for="stepOrder">Step Order</label>
                        <InputNumber id="stepOrder" @bind-Value="addJobStep.StepOrder" class="form-control form-control-sm" />
                        <ValidationMessage For="@(() => addJobStep.StepOrder)" />
                    </div>
                </div>
                <div class="row pt-2">
                    <div class="input-group">
                        <div class="pe-1">
                            <button type="submit" class="btn btn-sm btn-primary">
                                <span class="bi bi-plus-circle" aria-hidden="false"></span> Add
                            </button>
                        </div>
                        <div class="pe-1">
                            <button type="button" class="btn btn-sm btn-warning" @onclick="ClearJobStep">
                                <span class="bi bi-x-lg" aria-hidden="false"></span> Clear
                            </button>
                        </div>
                    </div>
                </div>
            </div>
        </EditForm>
    </div>
</div>
<div class="row">
    <div class="col-lg-12 mt-2">
        @* I have a quickgrid here that displays all the generated controls where I can edit or delete them. *@
    </div>
</div>
<div class="row">
    <div class="col-lg-12">
        <EditForm Model="@addDeliverable" OnValidSubmit="AddDeliverable">
            <DataAnnotationsValidator />
            <div class="container-fluid border border-1 rounded p-3" style="background-color:lightgray;">
                <div class="row">
                    <div class="col-lg-4">
                        <label for="prod">Prod/RMA</label>
                        <InputText id="prod" @bind-Value="addDeliverable.ProdRMA" class="form-control form-control-sm" />
                        <ValidationMessage For="@(() => addDeliverable.ProdRMA)" />
                    </div>
                    <div class="col-lg-4">
                        <label for="serialnumber">Serial#</label>
                        <InputText id="serialnumber" @bind-Value="addDeliverable.SerialNumber" class="form-control form-control-sm" />
                        <ValidationMessage For="@(() => addDeliverable.SerialNumber)" />
                    </div>
                    <div class="row">
                        @if (jobStepList != null)
{
    <div class="row">
        @foreach (var step in jobStepList)
        {
            <DynamicStep @key="@step.JobStepId" Id="@step.StepName" HandleNewStepEvent="HandleNewStepEvent"></DynamicStep>
        }
    </div>
}
                    </div>                                       
                    <div class="col-lg-3">
                        <div class="input-group mt-4">
                            <div class="pe-1">
                                <button type="submit" class="btn btn-sm btn-primary">
                                    <span class="bi bi-plus-circle" aria-hidden="false"></span> Add
                                </button>
                            </div>
                            <div class="pe-1">
                                <button type="button" class="btn btn-sm btn-warning" @onclick="ClearDeliverable">
                                    <span class="bi bi-x-lg" aria-hidden="false"></span> Clear
                                </button>
                            </div>
                        </div>
                    </div>
                </div> 
            </div>
        </EditForm>
    </div>
</div>


@code{
    private List<StepTitle>? stepTitles;
    private string stepId;
    
    protected override async Task OnInitializedAsync()
    {
        await base.OnInitializedAsync();

        dbContext = DbContextFactory.CreateDbContext();
        await LoadStepOptions();
        await LoadJobStepControls();
    }
    
    private async Task LoadStepOptions()
    {
        //This holds values like "Mechanical", "Final Prep", "Final Inspection", etc...
        stepTitles = await dbContext.StepTitles.ToListAsync();
    }

    private void HandleNewStepEvent(string extraStep)
    {
        //not sure what to do here since I can't tell which child control the value is    coming from
    }

    private async Task AddJobStepControl()
    {
        hideStepMessage = true;

        if (jobStepList != null && jobStepList.Count > 0)
        {
            bool alreadyExists = jobStepList.Any(x => x.StepName == addJobStep.StepName!.ToString());
            if (alreadyExists)
            {
                hideStepMessage = false;
                return;
            }
        }

        using var context = DbContextFactory.CreateDbContext();
        addJobStep.JobId = JobId;
        await context.JobSteps.AddAsync(addJobStep);
        await context.SaveChangesAsync();

        await LoadJobStepControls();
        addJobStep = new();
    }

    private async Task LoadJobStepControls()
    {
        jobStepList = await dbContext.JobSteps.Where(x => x.JobId == JobId).ToListAsync();
    }
}

3 Replies 3

"If I'm on the wrong path". My advice is trying to create a dynamic form is the wrong path. You're chasing the pot of gold at the end of a rainbow. It looks simple when you just have to deal with text fields, but as the number of field types grow it becomes horribly complex. Why do you think the Internet is not awash with Dynamic Form libraries?

Damn, I was hoping that wouldn't be the answer. Back to the drawing board to figure out how to meet the objectives given to me.

you'd just tie the form to a plain old class object... with arrays for the dynamic-added stuff. So bind to the class object and when user chooses to add, you call a function which adds a new empty class to the array. HTMLHelpers like editor-for etc... are helpful here. Not familiar with Blazor, but I'm sure the methods are very similar to standard mvc/razor stuff. Using parent/child is not necessary.

Your Reply

By clicking “Post Your Reply”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.