More Choices

More Choices

I figure choices are usually a good thing. For instance, I recently fleshed out my Blazor form generation library with some nice, added features. The goal, in making those changes, was to give developers more ways to do the same things. More choices are almost always a good thing.

What do I mean by “more choices”? Let’s go look at an example.

Suppose we needed to generate a simple form with a tab control, and two tabs, where each tab contains dynamically generated form content. Everything in the tab control should be inside the form tag, and therefore, all validated and submitted as a single transaction. The end result should look something like this:

Now granted, this is a hypothetical example. Still, I did need to do something like this, for one of my Blazor plugins, so, it’s not altogether theoretical.

So, how would we do this?

Well, the first way is to use the DynamicForm component, and have it generate everything for us. Here us what the markup would look like:

@page "/example1"

<DynamicForm Model="@Model" OnValidSubmit="OnValidSubmit" />

@code { 

    private MyVM Model { get; set; } = new MyVM();

    private void OnValidSubmit(EditContext editContext)
    {
        // TODO : do something with the submit.
    }

    [RenderMudTabs]
    public class MyVM
    {
        [RenderMudTabPanel(Text = "Panel A")]
        public ModelA A { get; set; } = new ModelA();

        [RenderMudTabPanel(Text = "Panel B")]
        public ModelB B { get; set; } = new ModelB();
    }

    public class ModelA
    {
        [RenderMudTextField]
        public string A1 { get; set; } = "A1 value";
    }

    public class ModelB
    {
        [RenderMudTextField]
        public string B1 { get; set; } = "B1 value";
    }
}

The markup couldn’t be simpler, just the single call to DynamicForm. The Model attribute is pointed at our Model property. The callback for the OnValidSubmit handler is pointed to our OnValidSubmit method. So far, so good.

The MyVM model has a few attributes applied to it that we need to go over. The first is the RenderMudTabs attribute. That attribute instructs the form generator to wrap the content, for the MyVM model, inside a MudTabs control.

Since we’ve started by rendering a MudTabs control, it stands to reason that our next step should be to render a couple of MudTabPanel controls. right? The RenderMudTabPanel attributes, on the A and B properties of the MyVM class, take care of that. Each RenderMudTabPanel attribute instructs the form generator to render that property inside a MudTabPanel control.

For my example, I chose to give each tab a single text box field. Just so the tab would have something to display. So, as we can see, the ModelA and ModelB classes each have a single string property, decorated with a corresponding RenderMudTextField attribute. The RenderMudTextField attribute instructs the form generator to render the string property using a MudTextField control, that is then bound to the property, on the model.

That constitutes one way of generating dynamic content inside a tab control. Let’s go look at another way now.


Suppose, for the sake of argument, that we needed to manually generate parts of a form, and dynamically generate other parts. Further, let’s say that we need to end result to look exactly the same as the first example:

How might we do that?

One way is to drop the use of the DynamicForm, which generates the actual form, as well as any dynamic content for the form. Instead, let’s use the DynamicContent component, which only generates the dynamic content and leaves everything else up to us. Let’s see what that might look like:

@page "/example2"

<EditForm Model="@Model" OnValidSubmit="OnValidSubmit">
    <MudTabs>
        <MudTabPanel Text="Panel A">
            <DynamicContent Model="@Model.A"/>
		</MudTabPanel>
        <MudTabPanel Text="Panel B">
            <DynamicContent Model="@Model.B"/>
		</MudTabPanel>
	</MudTabs>
    <button type="submit">Submit</button>
</EditForm>

@code { 

    private MyVM Model { get; set; } = new MyVM();

    private void OnValidSubmit(EditContext editContext)
    {
        // TODO : do something with the submit.
    }

    public class MyVM
    {
        [RenderObject]
        public ModelA A { get; set; } = new ModelA();

        [RenderObject]
        public ModelB B { get; set; } = new ModelB();
    }

    public class ModelA
    {
        [RenderMudTextField]
        public string A1 { get; set; } = "A1 value";
    }

    public class ModelB
    {
        [RenderMudTextField]
        public string B1 { get; set; } = "B1 value";
    }
}

In this second example, we’re now handling the EditForm tag ourselves. We still point the Model attribute at our Model property. We still wire up a handler for the OnValidSubmit callback. The difference is, this time, we have absolute control over our form, and, we STILL get to dynamically generate our form content, using the DynamicContent components.

We’ve manually built up the MudTabs control, with the two MudTabPanel controls. Each MudTabPanel then contains a single DynamicContent component whose Model attribute is pointed to a particular part of the parent model.

The attributes we’ve used are a little different, for the MyVM class. Since we aren’t asking the form generator to create the tabs, there isn’t a RenderMudTabs attribute on the MyVM class definition. Also, since we aren’t asking the form generator to create the tab panels, we’ve swapped out the RenderMudTabPanel attributes for RenderObject attributes. The RenderObject attribute instructs the form generator to simply render the content for an object property.

So, why manually build up the form and the tabs when we can get the form generator to handle all that for us?? Well, think about it, we might need to integrate with a library that I haven’t, as yet, created support for. Or, we might need to add additional content to the HTML – perhaps an additional icon in the tabs, for instance.

Also, we might need to add a validator to the form that I haven’t, as yet, added support for. That’s another perfectly valid reason to render the form manually.


The other way we could have handled our hypothetical example, is to go create our own version of RenderMudTabs and RenderMudTabPanel attributes. I’ll write about that at some point, but not today. for now, just know that writing your own attributes is another way to get things done.

I hope I’ve demonstrated that my Blazor form generation library is flexible, and extensible, and easily capable of handling both simple form generation scenarios, as well as not so simple.

As always, all of my source code is available, for free, HERE.

All of my NUGET packages are available, for free. HERE.

Photo by Beth Macdonald on Unsplash