Skip to content

Commit

Permalink
changed how the templating works for inheriting QuickForm
Browse files Browse the repository at this point in the history
added validation docs.
  • Loading branch information
ddjerqq committed Mar 5, 2024
1 parent 905da73 commit f1b945e
Show file tree
Hide file tree
Showing 18 changed files with 234 additions and 215 deletions.
141 changes: 133 additions & 8 deletions docs/customization.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,136 @@
---
title: Customization
---

# Customization

While being effortless to use, %product% provides support for a wide variety of customizations.
such as:
- Css Class customization
- Validation
- Custom data types
- Custom Input / Label / Description / Valid-Invalid feedback / Submit Button customization
While being effortless to use, QuickForm provides support for a wide variety of customization.

There are a few ways to customize QuickForm:

- **[Using attributes](../attributes)**: You can use the `attributes` to decorate the individual fields and their looks.
- **[Customizing the whole layout](#custom-layout)**: You can create your own custom proxy component and use the `QuickForm` component
inside it.

---

## Custom layout

This is a step-by-step guide to creating a custom layout for your app.
Please note, that this process may seem a little complex for users who are not familiar with Blazor.

---


## Define the directives

``` jsx title="AppForm.razor - declaring directives" linenums="1"
@using QuickForm.Components // (1)!
@inherits QuickForm<TEntity> // (2)!
@typeparam TEntity where TEntity : class, new() // (3)!
```

1. The `@using` directive is used to import the namespace of the `QuickForm` component.
This is necessary to use the `QuickForm` component in the current file.
2. The `@inherits` directive is used to specify the base class for the component. This is the class that contains the logic for the
component.
In this case, we are inheriting from the `QuickForm` component, which is the base class for the QuickForm component.
3. The `@typeparam` directive is used to specify that the component will be generic and work with any type,
as long as it satisfies the constraints specified in the `where` clause.

---


## Create the layouts

``` jsx title="AppForm.razor - setting parameters and field layouts" linenums="5" hl_lines="3-4 8-24 28-30"
@{
// Additional attributes, in this case, it is the class to be applied to the form.
AdditionalAttributes ??= new Dictionary<string, object>();
AdditionalAttributes.Add("class", "flex flex-col");

// Field layout
ChildContent = context => // (1)!
@<div class="flex flex-col">
<label for="@context.EditorId"> // (2)!
@context.DisplayName // (3)!
</label>

@context.InputComponent("peer") // (4)!

<span class="text-gray-500">
@context.Description // (5)!
</span>

<span class="hidden peer-[.valid]:block text-green-700">
@context.ValidFeedback // (6)!
</span>

@context.ValidationMessages("hidden peer-[.invalid]:block text-red-700") // (7)!
</div>;

// Submit button layout
SubmitButtonTemplate =
@<button type="submit" class="border border-green-500 text-green-500">
submit
</button>;
}
```
1. The markup inside the `ChildContent` fragment, will be applied to individual fields.
The `@context` object is used to access the field's metadata and the input component. It is an object which
implements [IQuickFormField](../api/QuickForm.Components.IQuickFormField).
2. `@context.EditorId` is a string, which holds the id of the input field. This can be used to associate the label with the input field.
It is automatically generated
3. `@context.DisplayName` is a string, which holds the display name for the field. This can be set using the `[DisplayName]` attribute.
4. `@context.InputComponent` is a [RenderFragment&lt;string&gt;](https://blazor-university.com/templating-components-with-renderfragements/)
that renders the input field. The string parameter for this RenderFragment is the CSS class to be applied to the input field.
5. `@context.Description` is a string, which holds the description for the field. This can be set using the `[Description]` attribute.
6. `@context.ValidFeedback` is a string, which holds the valid feedback for the field. This can be set using the `[ValidFeedback]` attribute.
7. Just like the `@context.InputComponent`, `@context.ValidationMessages` is also
a [RenderFragment&lt;string&gt;](https://blazor-university.com/templating-components-with-renderfragements/)
that renders the validation messages. The string parameter for this RenderFragment is the CSS class to be applied to the container
element containing validation messages.
!!! info "@context"
See [IQuickFormField]() interface for more information on the `@context` object and what fields are abstracted to you.
---
## **Important!** add the base component's markup
``` jsx title="AppForm.razor - parent markup rendering" linenums="37" hl_lines="2"
@{
base.BuildRenderTree(__builder); // (1)!
}
```
1. This line is used to call your custom component's base class's - QuickForm's `BuildRenderTree` method.
!!! warning "Important"
This is necessary to render the form and its fields. **Without this line your custom form will not be rendered.**
---
## Congratulations!
You can now use your component all around your application!
Here is what a minimally styled TailwindCSS form looks like:
![your custom form](images/customized-form.png)
---
# Next steps
<div class="grid cards" markdown>
- :octicons-check-24: [Check out validation](../validation)
- :material-format-font: [Check out the attributes](../attributes)
> <img src="full-example.png" alt="full example" />
{title="Most types of customization on one form" style="note"}
</div>
8 changes: 4 additions & 4 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ public class Person

---

# Create the form
## Create the form

=== "Tailwind"

Expand Down Expand Up @@ -135,7 +135,7 @@ public class Person
---


# Congratulations!
## Congratulations!

You now have a fully functional form with validation and submit handling.

Expand All @@ -147,10 +147,10 @@ You now have a fully functional form with validation and submit handling.

![The generated form](images/bs-user.png){ loading=lazy }


---

# Next steps

## Next steps

<div class="grid cards" markdown>

Expand Down
Binary file added docs/images/customized-form.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,6 @@ But with time, it quickly grew into a passionate project which I believe will sa
Big thanks to [Anton Toshik](https://www.youtube.com/@RawCoding "The man, the legend behind Raw Coding"),
for inspiration about Expressions and Reflection.

[blazor-university](https://blazor-university.com/templating-components-with-renderfragements/) best place to learn blazor!

And thank **you**, for using QuickForm! 💗
5 changes: 4 additions & 1 deletion mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@ theme:
- search.suggest
- search.highlight
- content.tabs.link
- content.code.annotation
- content.code.copy
- content.code.select
- content.code.annotate
- content.tooltips

palette:
Expand Down Expand Up @@ -55,6 +56,8 @@ markdown_extensions:
generic: true
- pymdownx.highlight:
anchor_linenums: true
line_spans: __span
pygments_lang_class: true
- pymdownx.emoji:
emoji_index: !!python/name:material.extensions.emoji.twemoji
emoji_generator: !!python/name:materialx.emoji.to_svg
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@
/// Supplies the message for when the input is valid.
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public sealed class ValidMessageAttribute : Attribute
public sealed class ValidFeedbackAttribute : Attribute
{
/// <summary>
/// Initializes a new instance of the <see cref="ValidMessageAttribute"/> class.
/// Initializes a new instance of the <see cref="ValidFeedbackAttribute"/> class.
/// </summary>
/// <param name="message">The message for valid feedback.</param>
public ValidMessageAttribute(string message) => Message = message;
public ValidFeedbackAttribute(string message) => Message = message;

/// <summary>
/// Gets the css class.
Expand Down
2 changes: 1 addition & 1 deletion src/QuickForm/Common/PropertyInfoExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ internal static string DisplayName(this PropertyInfo prop)

internal static string? ValidFeedbackText(this PropertyInfo prop)
{
if (prop.GetCustomAttribute<ValidMessageAttribute>() is not { Message: var message })
if (prop.GetCustomAttribute<ValidFeedbackAttribute>() is not { Message: var message })
return null;

return message;
Expand Down
37 changes: 23 additions & 14 deletions src/QuickForm/Components/BsQuickForm.razor
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
@typeparam TEntity where TEntity : class, new()
@inherits QuickForm<TEntity>

<QuickForm @ref="@QuickFormRef" Model="Model"
ValidClass="is-valid" InvalidClass="is-invalid"
class="d-flex flex-column text-white text-start gap-3 rounded border p-3 bg-dark">
<ChildContent>
<div class="d-flex flex-column">
@{
AdditionalAttributes ??= new Dictionary<string, object>
{
{
"class",
"d-flex flex-column text-white text-start gap-3 rounded border p-3 bg-dark"
}
};

ChildContent = context =>
@<div class="d-flex flex-column">
<label for="@context.EditorId" class="fw-bold">
@context.DisplayName
</label>
Expand All @@ -26,12 +33,14 @@
}

@context.ValidationMessages("invalid-feedback text-start text-danger fw-bold")
</div>
</ChildContent>

<SubmitButtonTemplate>
<button type="submit" class="btn btn-outline-success text-success fw-bold py-2 px-4 rounded">
Login
</button>
</SubmitButtonTemplate>
</QuickForm>
</div>;

SubmitButtonTemplate =
@<button type="submit" class="btn btn-outline-success text-success fw-bold py-2 px-4 rounded">
submit
</button>;
}

@{
base.BuildRenderTree(__builder);
}
61 changes: 0 additions & 61 deletions src/QuickForm/Components/BsQuickForm.razor.cs

This file was deleted.

3 changes: 1 addition & 2 deletions src/QuickForm/Components/QuickForm.razor
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
@using Microsoft.AspNetCore.Components.Forms
@using Blazored.FluentValidation
@using QuickForm.Common

@typeparam TEntity where TEntity : class, new()

<EditForm EditContext="EditContext"
OnSubmit="@OnSubmit" OnInvalidSubmit="@OnInvalidSubmit" OnValidSubmit="@OnValidSubmit"
AdditionalAttributes="AdditionalAttributes">
AdditionalAttributes="AdditionalAttributes?.AsReadOnly()">
<DataAnnotationsValidator/>
<FluentValidationValidator/>

Expand Down
Loading

0 comments on commit f1b945e

Please sign in to comment.