Skill v1.0.0
currentAutomated scan100/100version: "1.0.0" name: orchardcore-forms description: Skill for building and managing forms in Orchard Core using the OrchardCore.Forms module. Covers form widget content types, form validation, form submissions with workflows, anti-forgery tokens, and custom form elements. Use this skill when requests mention Orchard Core Forms, Create and Configure Forms, Enabling the Forms Feature, Form Widget Content Types, Form Content Type Settings, Input Element Configuration, or closely related Orchard Core implementation, setup, extension, or troubleshooting work. Strong matches include work with OrchardCore.Forms, OrchardCore.Workflows, OrchardCore.Flows, OrchardCore.Widgets, OrchardCore.Workflows.Http, OrchardCore.ContentManagement, OrchardCore.DisplayManagement.Views, OrchardCore.Modules, OrchardCore.ContentManagement.Metadata. It also helps with forms examples, Form Content Type Settings, Input Element Configuration, Input Element Properties, plus the code patterns, admin flows, recipe steps, and referenced examples captured in this skill. license: Apache-2.0 metadata: author: CrestApps Team version: "1.0"
Orchard Core Forms - Prompt Templates
Create and Configure Forms
You are an Orchard Core expert. Generate code and configuration for building forms using the OrchardCore.Forms module.
Guidelines
- Enable the
OrchardCore.Formsfeature to use form widgets in content layouts. - Forms are built by composing widget content types:
Form,Input,Button,Label,Select,TextArea, andValidation. - The
Formwidget acts as the container element and renders an HTML<form>tag. - Each form element widget maps to a standard HTML form element with configurable attributes.
- Use the
FormInputElementPartandFormElementPartto configure element names, IDs, and CSS classes. - Anti-forgery tokens are included automatically in forms rendered by the
Formwidget. - Pair forms with the
OrchardCore.Workflowsmodule to handle form submissions via theHttpRequestEventor form-specific workflow events. - Form validation is handled via
Validationwidgets that display model state errors for specific fields. - Place form element widgets inside a
Formwidget using aFlowPartor zone-based layout.
Enabling the Forms Feature
{"steps": [{"name": "Feature","enable": ["OrchardCore.Forms","OrchardCore.Flows","OrchardCore.Widgets"],"disable": []}]}
Form Widget Content Types
The OrchardCore.Forms module provides the following widget content types:
| Content Type | HTML Element | Purpose | |
|---|---|---|---|
Form | <form> | Container for all form elements. Configures method, action URL, and encoding type. | |
Input | <input> | Text, email, password, hidden, number, and other standard input types. | |
Button | <button> | Submit, reset, or custom buttons. | |
Label | <label> | Labels associated with form inputs via the for attribute. | |
Select | <select> | Dropdown lists and multi-select fields. | |
TextArea | <textarea> | Multi-line text input fields. | |
Validation | <span> | Displays validation error messages for a specific input element. | |
ValidationSummary | <div> | Displays a summary of all validation errors in the form. |
Form Content Type Settings
The Form widget has these key settings:
- Action - The URL the form submits to. Leave empty to post back to the current page.
- Method - HTTP method:
GETorPOST(default isPOST). - Encoding Type - The
enctypeattribute:application/x-www-form-urlencoded,multipart/form-data, ortext/plain. - Enable Anti-forgery Token - When enabled, an anti-forgery token is automatically included in the form.
- Workflow Type ID - Associates a workflow to execute when the form is submitted.
Input Element Configuration
The Input widget supports these HTML input types:
text- Standard single-line text input.email- Email address input with browser validation.password- Masked password input.number- Numeric input with optional min/max.hidden- Hidden input for passing data.checkbox- Boolean checkbox input.radio- Radio button for single selection.url- URL input with browser validation.tel- Telephone number input.date- Date picker input.color- Color picker input.
Input Element Properties
Each input element has the following configurable properties via FormInputElementPart:
- Name - The
nameattribute used for form submission and model binding. - Type - The HTML input type (text, email, password, number, hidden, etc.).
- Placeholder - Placeholder text displayed when the field is empty.
- Default Value - The initial value of the input field.
And via FormElementPart:
- Id - The
idattribute for the HTML element. - CSS Class - Additional CSS classes applied to the element.
Form Validation
Using Validation Widgets
Add a Validation widget after each form input to display field-level error messages. Set the For property to the name attribute of the input element it validates.
Add a ValidationSummary widget at the top or bottom of the form to display a summary list of all validation errors.
Server-Side Validation in Workflows
When processing form submissions in a workflow, use the ValidateAntiforgeryTokenTask activity to verify the anti-forgery token. Use a ScriptTask to perform custom validation and add model state errors:
// In a ScriptTask within a workflowvar name = requestFormAsDict["Name"];if (!name || name.trim() === "") {addModelError("Name", "The Name field is required.");}var email = requestFormAsDict["Email"];if (!email || !email.includes("@")) {addModelError("Email", "Please enter a valid email address.");}if (modelState.isValid) {setOutcome("Valid");} else {setOutcome("Invalid");}
Anti-Forgery Token Handling
- The
Formwidget automatically generates and embeds a__RequestVerificationTokenhidden field when anti-forgery is enabled. - When building custom form handlers or controllers, always validate the token using
[ValidateAntiForgeryToken]or by includingValidateAntiforgeryTokenTaskin your workflow. - For AJAX form submissions, include the token in the request header as
RequestVerificationToken.
Form Submissions with Workflows
Configuring a Form Workflow
- Enable
OrchardCore.WorkflowsandOrchardCore.Workflows.Http. - Create a workflow with an
HttpRequestEventorFormSubmissionEventas the starting activity. - Add a
ValidateAntiforgeryTokenTaskas the first task after the event. - Use
ScriptTaskactivities to read form data and perform validation. - Use
NotifyTaskto display success or error messages. - Use
HttpRedirectTaskto redirect after successful submission.
Reading Form Data in Workflows
Access submitted form values through the requestFormAsDict object in workflow scripts:
// Access form field values by their name attributevar firstName = requestFormAsDict["FirstName"];var lastName = requestFormAsDict["LastName"];var email = requestFormAsDict["Email"];var message = requestFormAsDict["Message"];// Set workflow properties for use in subsequent activitiessetProperty("FullName", firstName + " " + lastName);setProperty("Email", email);setProperty("Message", message);setOutcome("Done");
Sending Email from Form Submissions
After reading and validating form data, use a SendEmailTask to process the submission:
- Set Subject, Body, Recipients using Liquid templates that reference workflow properties.
- Example body template:
{{ Workflow.Properties.FullName }} submitted: {{ Workflow.Properties.Message }}
Creating Custom Form Elements
Custom Form Element Content Part
using OrchardCore.ContentManagement;public sealed class RatingFieldPart : ContentPart{public int MinValue { get; set; } = 1;public int MaxValue { get; set; } = 5;public int DefaultValue { get; set; } = 3;public string Label { get; set; }public string Name { get; set; }}
Custom Form Element Display Driver
using OrchardCore.ContentManagement.Display.ContentDisplay;using OrchardCore.ContentManagement.Display.Models;using OrchardCore.DisplayManagement.ModelBinding;using OrchardCore.DisplayManagement.Views;public sealed class RatingFieldPartDisplayDriver : ContentPartDisplayDriver<RatingFieldPart>{public override IDisplayResult Display(RatingFieldPart part, BuildPartDisplayContext context){return Initialize<RatingFieldPartViewModel>("RatingFieldPart", model =>{model.MinValue = part.MinValue;model.MaxValue = part.MaxValue;model.DefaultValue = part.DefaultValue;model.Label = part.Label;model.Name = part.Name;}).Location("Detail", "Content");}public override IDisplayResult Edit(RatingFieldPart part, BuildPartEditorContext context){return Initialize<RatingFieldPartViewModel>("RatingFieldPart_Edit", model =>{model.MinValue = part.MinValue;model.MaxValue = part.MaxValue;model.DefaultValue = part.DefaultValue;model.Label = part.Label;model.Name = part.Name;}).Location("Content");}public override async Task<IDisplayResult> UpdateAsync(RatingFieldPart part,UpdatePartEditorContext context){var model = new RatingFieldPartViewModel();await context.Updater.TryUpdateModelAsync(model, Prefix);part.MinValue = model.MinValue;part.MaxValue = model.MaxValue;part.DefaultValue = model.DefaultValue;part.Label = model.Label;part.Name = model.Name;return Edit(part, context);}}
Registering a Custom Form Element
using OrchardCore.ContentManagement;using OrchardCore.ContentManagement.Display.ContentDisplay;using OrchardCore.Modules;public sealed class Startup : StartupBase{public override void ConfigureServices(IServiceCollection services){services.AddContentPart<RatingFieldPart>().UseDisplayDriver<RatingFieldPartDisplayDriver>();}}
Custom Form Element Migration
using OrchardCore.ContentManagement.Metadata;using OrchardCore.ContentManagement.Metadata.Settings;using OrchardCore.Data.Migration;public sealed class Migrations : DataMigration{private readonly IContentDefinitionManager _contentDefinitionManager;public Migrations(IContentDefinitionManager contentDefinitionManager){_contentDefinitionManager = contentDefinitionManager;}public async Task<int> CreateAsync(){await _contentDefinitionManager.AlterPartDefinitionAsync("RatingFieldPart", part => part.Attachable().WithDescription("Renders a rating input element in a form."));await _contentDefinitionManager.AlterTypeDefinitionAsync("RatingField", type => type.DisplayedAs("Rating Field").Stereotype("Widget").WithPart("RatingFieldPart"));return 1;}}
Form Element Placement and Styling
Placement in FlowPart
Form elements are arranged inside a Form widget using FlowPart. Each element widget is a child of the flow and is rendered in the order it appears.
Typical form structure:
Form(container with method, action, and encoding settings)
Label(for first input)Input(first input field)Validation(validation message for first input)Label(for second input)TextArea(second input field)Validation(validation message for second input)ValidationSummary(summary of all errors)Button(submit button)
Applying CSS Classes
Use the FormElementPart CSS Class property to apply styling classes:
- Bootstrap classes:
form-control,form-select,btn btn-primary,form-label. - Custom CSS classes for project-specific styling.
- Wrapper classes for layout:
mb-3,row,col-md-6.
Select Element Options
The Select widget options are defined as a list of text entries with one option per line. Each line uses the format value=Display Text or simply Display Text (where the display text is used as both value and label).
Button Types
The Button widget supports these types:
submit- Submits the form.reset- Resets form fields to default values.button- A generic button for custom JavaScript actions.
Recipe Configuration for Forms
Creating a Contact Form via Recipe
{"steps": [{"name": "Feature","enable": ["OrchardCore.Forms","OrchardCore.Flows","OrchardCore.Widgets","OrchardCore.Workflows","OrchardCore.Workflows.Http"],"disable": []}]}
Content Definition for a Form Page
{"steps": [{"name": "ContentDefinition","ContentTypes": [{"Name": "FormPage","DisplayName": "Form Page","Settings": {"ContentTypeSettings": {"Creatable": true,"Listable": true,"Draftable": true}},"ContentTypePartDefinitionRecords": [{"PartName": "TitlePart","Name": "TitlePart","Settings": {"ContentTypePartSettings": {"Position": "0"}}},{"PartName": "AutoroutePart","Name": "AutoroutePart","Settings": {"ContentTypePartSettings": {"Position": "1"},"AutoroutePartSettings": {"AllowCustomPath": true,"Pattern": "{{ ContentItem | display_text | slugify }}"}}},{"PartName": "FlowPart","Name": "FlowPart","Settings": {"ContentTypePartSettings": {"Position": "2"}}}]}]}]}