Skill: angular-dynamic-forms
Descrição
Use quando o usuário pedir para criar, configurar ou modificar formulários dinâmicos usando a lib @Rodrigos-dev/ars-dynamic-form neste projeto Angular + Ionic.
Documenta: Configuração de campos, validações, mascaras, multi-step, persistência LocalStorage, e padrões arquiteturais.
📦 Dependência
"@Rodrigos-dev/ars-dynamic-form": "npm:@Rodrigos-dev/ars-dynamic-form@^0.0.90"
Peer Dependencies: Angular 18/19/20, Ionic 8, Capacitor 6/7/8
🗂️ Estrutura de Pastas
src/app/
├── features/[feature]/pages/[page]/
│ ├── [page].page.ts
│ ├── [page].page.html
│ ├── [page].page.scss
│ └── forms-configs/ ← PASTA DO FORMULÁRIO
│ └── [nome]-form-config-fields.ts ← CONFIG DOS CAMPOS
│
├── shared/constants/forms/
│ ├── form-base-configs-default.ts ← DEFAULTS GLOBAIS
│ ├── forms-names.constants.ts ← NOMES DE STORAGE
│ └── forms-rules-regex-password.ts ← REGEX SENHA
🔧 Imports Necessários
// Componentes e interfaces
import {
DynamicFormComponent,
IDynamicFormFieldsConfig,
IDynamicFormContainerConfig,
EFieldDynamicForm,
EInputModeField,
EMaskInputs,
ValidatorDocumentsAndMore,
Base_Model_4_Initial_Model_Form_Defaults_Fields_Configs
} from '@Rodrigos-dev/ars-dynamic-form';
📐 Interface Principal: IDynamicFormFieldsConfig
interface IDynamicFormFieldsConfig {
name: string; // formControlName
typeFieldForm: EFieldDynamicForm; // tipo do campo
initialValue?: any; // valor inicial
disabled?: boolean; // desabilitado
validations?: ValidatorFn | ValidatorFn[]; // validators Angular
defaultAllFieldsConfigs?: IDefaultAllFieldsConfigs; // grid, bordas
itemContainerFieldsConfigs?: IItemContainerFieldsConfigs; // padding
baseFieldsConfigs?: IBaseConfigsFormFields; // config específica do tipo
}
🎨 Tipos de Campo Disponíveis (EFieldDynamicForm)
| Tipo | Uso |
|---|---|
INPUT |
Campo de texto (email, password, text, tel, number) |
INPUT_NUMBER |
Campo numérico |
SELECT |
Dropdown/select |
TEXTAREA |
Área de texto |
CHECKBOX |
Checkbox |
DATE |
Seletor de data |
TIME |
Seletor de hora |
DATE_TIME |
Data + Hora |
SWITCH |
Toggle/switch |
AVATAR |
Upload de imagem |
TERMS_POLICY |
Termos e políticas |
SELECT_USER |
Seleção de usuário |
DIVIDER |
Separador visual com título |
RADIO |
Radio button |
IMAGE |
Upload de imagem |
VIDEO |
Upload de vídeo |
VIDEOIMAGEORDOCUMENTS |
Upload misto |
SIGNATURE |
Assinatura digital |
📝 Exemplos Reais de Configuração
Campo INPUT (Email)
{
name: 'email',
typeFieldForm: EFieldDynamicForm.INPUT,
validations: [Validators.required, Validators.email],
initialValue: undefined,
disabled: undefined,
defaultAllFieldsConfigs: defaultAllFieldsConfigsUse,
itemContainerFieldsConfigs: itemContainerFieldsConfigUse,
baseFieldsConfigs: {
inputConfigs: {
eInputModeField: EInputModeField.EMAIL,
placeholderInput: 'Enter your email',
labelInput: 'Email',
iconNameInput: 'mail',
iconFontSizeInput: '16px',
},
},
}
Campo INPUT (Password) com Botões Abaixo
{
name: 'password',
typeFieldForm: EFieldDynamicForm.INPUT,
validations: [Validators.required, Validators.minLength(6)],
defaultAllFieldsConfigs: defaultAllFieldsConfigsUse,
itemContainerFieldsConfigs: itemContainerFieldsConfigUse,
baseFieldsConfigs: {
inputConfigs: {
eInputModeField: EInputModeField.PASSWORD,
placeholderInput: 'Enter password',
labelInput: 'Password',
showOrHideDataInFieldPassword: false,
},
buttonOneDownField: { // Checkbox "Remember Password"
showButtonOneDownField: true,
isCheckbox: true,
checkboxValue: false,
textButtonOneDownField: 'Remember Password',
nameCheckBoxToVerify: 'rememberPassword',
},
buttonTwoDownField: { // Link "Forgot password?"
showButtonTwoDownField: true,
textButtonTwoDownField: 'Forgot password?',
linkButtonTwoDownField: '/auth/forgot-password',
},
},
}
Campo INPUT com Máscara (CPF)
{
name: 'document',
typeFieldForm: EFieldDynamicForm.INPUT,
validations: [Validators.required, ValidatorDocumentsAndMore.cpf()],
baseFieldsConfigs: {
inputConfigs: {
eInputModeField: EInputModeField.TEXT,
maskInput: EMaskInputs.Cpf, // Máscara automática CPF
placeholderInput: 'Enter your CPF',
labelInput: 'CPF',
iconNameInput: 'document-text',
},
},
}
Campo INPUT com Validação de Igualdade
{
name: 'confirmPassword',
typeFieldForm: EFieldDynamicForm.INPUT,
validations: [Validators.required, Validators.minLength(6)],
baseFieldsConfigs: {
inputConfigs: {
eInputModeField: EInputModeField.PASSWORD,
placeholderInput: 'Confirm Password',
labelInput: 'Confirm Password',
valueEqualWithFieldFormName: 'password', // Compara com campo 'password'
},
},
}
Campo SELECT (Estados Brasileiros)
{
name: 'state',
typeFieldForm: EFieldDynamicForm.SELECT,
initialValue: '',
defaultAllFieldsConfigs: { ...defaultAllFieldsConfigsUse, sizeGridMainColumn: 6 },
baseFieldsConfigs: {
selectConfigs: {
selectLabel: 'State',
selectPlaceholder: 'Select state',
selectOptions: BRAZILIAN_STATES, // [{ value: 'SP', label: 'São Paulo' }, ...]
selectModeType: 'single',
},
},
}
Campo SWITCH (Toggle)
{
name: 'receive_notifications',
typeFieldForm: EFieldDynamicForm.SWITCH,
initialValue: false,
defaultAllFieldsConfigs: defaultAllFieldsConfigsUse,
itemContainerFieldsConfigs: { ...itemContainerFieldsConfigUse, marginTopField: '10px' },
baseFieldsConfigs: {
toggleConfigs: {
toggleLabel: 'Receive Notifications',
toggleNote: 'Receive email notifications about updates',
toggleFill: 'outline',
toggleLabelPlacement: 'stacked',
},
},
}
DIVIDER (Título Visual)
{
typeFieldForm: EFieldDynamicForm.DIVIDER,
name: 'title',
defaultAllFieldsConfigs: defaultAllFieldsConfigsUse,
itemContainerFieldsConfigs: { ...itemContainerFieldsConfigUse, paddingStart: '15px' },
itemDividerConfigs: {
backgroundItemItemDivider: 'transparent',
textAlignLabelItemDivider: 'left',
colorLabelItemDivider: 'var(--ion-color-primary)',
fontWeightLabelItemDivider: '900',
fontSizeLabelItemDivider: '1.5rem',
textLabelItemDivider: 'Welcome!',
subTextLabelItemDivider: 'Sign in to continue',
fontSizeSubTextItemDivider: '14px',
colorSubTextItemDivider: 'var(--ion-color-secondary)',
},
}
Grid Layout (2 Colunas)
// Campo ocupa metade (6 de 12 colunas):
defaultAllFieldsConfigs: { ...defaultAllFieldsConfigsUse, sizeGridMainColumn: 6 },
// Campo ocupa largura total (12 de 12):
defaultAllFieldsConfigs: { ...defaultAllFieldsConfigsUse, sizeGridMainColumn: 12 },
🎯 Padrão de Uso no Template (HTML)
Formulário Simples (Login)
<mb-dynamic-form
[formConfigFields]="ILoginFormConfigs"
(formValueChange)="onFormChange($event)"
(listenFormValidStatus)="listenFormValidStatus($event)"
[mbFormStorageName]="{
name: keyNameFormStorage,
encrypt: false,
key: secretKey,
enabled: rememberPassword()
}"
[formContainerConfig]="formContainerConfig"
></mb-dynamic-form>
Formulário Multi-Step (Register)
<!-- Step 1: Dados Pessoais -->
<mb-dynamic-form
*ngIf="renderUserForm()"
[formConfigFields]="userFormfieldsConfig"
[formContainerConfig]="formContainerConfig"
(formValueChange)="onFormChange($event, 'user')"
(listenFormValidStatus)="listenFormValidStatus($event, 'user')"
[mbFormStorageName]="{ name: 'undefined', encrypt: false, key: 'secretKey', enabled: false }"
></mb-dynamic-form>
<!-- Step 2: Endereço -->
<mb-dynamic-form
*ngIf="renderAddressForm()"
[formConfigFields]="addressFormfieldsConfig"
[formContainerConfig]="formContainerConfig"
(formValueChange)="onFormChange($event, 'address')"
(listenFormValidStatus)="listenFormValidStatus($event, 'address')"
[mbFormStorageName]="{ name: 'undefined', encrypt: false, key: 'secretKey', enabled: false }"
></mb-dynamic-form>
Formulário com Reference (Acesso Direto)
<mb-dynamic-form
#formPassword
[formConfigFields]="forgotPasswordFormConfigs"
(formValueChange)="onFormChange($event, 'updateForgotPassword')"
(listenFormValidStatus)="listenFormValidStatus($event)"
[mbFormStorageName]="undefined"
[formContainerConfig]="formContainerConfig"
></mb-dynamic-form>
🎯 Padrão de Uso no Component (TypeScript)
Padrão Simples (Login)
export class LoginPage {
// 1. Config do form como função que retorna array
ILoginFormConfigs = LOGIN_FORM_CONFIG();
formContainerConfig = FORM_CONTAINER_CONFIG_DEFAULT;
// 2. Signals para estado
isFormValid = signal(false);
isLoading = signal(false);
// 3. Callback de mudança de valor
onFormChange(form: ILoginForm) {
this.submitForm = { email: form.email, password: form.password };
}
// 4. Callback de validade
listenFormValidStatus(isValid: boolean) {
this.isFormValid.set(isValid);
}
// 5. Submit - só funciona se form válido
async login(): Promise<void> {
if (!this.isFormValid() || this.isLoading()) return;
// ... chamar API
}
}
Padrão Multi-Step (Register)
// Dados retidos em memória entre steps
private userData: any = {};
private addressData: any = {};
// Rehidratação - injeta valores salvos no initialValue
private rehydrateFormConfigs(formType: 'user' | 'address'): void {
const dataToApply = formType === 'user' ? this.userData : this.addressData;
const configsToUpdate = formType === 'user'
? this.userFormfieldsConfig
: this.addressFormfieldsConfig;
configsToUpdate.forEach((field) => {
if (dataToApply[field.name] !== undefined) {
field.initialValue = dataToApply[field.name];
}
});
}
// Truque para destruir/recriar o form:
this.renderUserForm.set(false); // remove do DOM
this.currentStep--;
setTimeout(() => {
this.renderUserForm.set(true); // recria limpo com dados rehidratados
}, 0);
Acesso Direto ao FormGroup (ForgotPassword)
@ViewChild('formPassword') passwordFormComponent!: DynamicFormComponent;
// Acessa o FormGroup interno da lib
const formGroup = this.passwordFormComponent?.form;
const isPasswordValid = !!formGroup.get('password')?.valid;
const confirmControl = formGroup.get('confirmPassword');
// Habilita/desabilita controle programaticamente
if (isPasswordValid) {
confirmControl?.enable({ emitEvent: false });
} else {
confirmControl?.disable({ emitEvent: false });
confirmControl?.setValue('', { emitEvent: false });
}
// Modifica a config (afeta o template)
confirmField.disabled = !isPasswordValid;
Rehidratação Dinâmica via Effect
private readonly userEffect = effect(() => {
const currentStoreUser = this.userStore.user();
this.emailToCodeFormConfigs.forEach((field) => {
if (field.name === 'email') {
field.initialValue = user.email;
field.baseFieldsConfigs.inputConfigs.readonlyInput = true;
field.baseFieldsConfigs.inputConfigs.obfuscateField = true;
}
});
// Destroi e recria o form
this.codeFormRender.set(false);
setTimeout(() => this.codeFormRender.set(true), 0);
});
🔐 Configuração de Storage (LocalStorage)
interface IMbFormStorageConfig {
name: string; // chave no LocalStorage
encrypt?: boolean; // criptografia AES
key?: string; // chave secreta para cifrar
enabled: boolean; // ativa/desativa
}
Uso no Login (Lembrar senha):
[mbFormStorageName]="{
name: keyNameFormStorage, // 'loginForm'
encrypt: false,
key: secretKey, // environment.secretEncrypt
enabled: rememberPassword() // signal reativo
}"
Uso sem persistência:
[mbFormStorageName]="undefined"
✅ Validadores Customizados (ValidatorDocumentsAndMore)
ValidatorDocumentsAndMore.cpf() // CPF (11 dígitos)
ValidatorDocumentsAndMore.cnpj() // CNPJ (14 dígitos)
ValidatorDocumentsAndMore.rg() // RG (9 dígitos, suporta 'X')
ValidatorDocumentsAndMore.cep() // CEP (8 dígitos)
ValidatorDocumentsAndMore.cpfCnpj() // Híbrido (detecta pelo tamanho)
🔑 Regex Rules para Senha
// forms-rules-regex-password.ts
export const RULES_REGEX_PASSOWORD_FORM_CONFIGS = [
{ regex: '.{6,}', message: 'Minimum 6 characters' },
{ regex: '[A-Z]', message: 'At least one uppercase letter' },
{ regex: '[0-9]', message: 'At least one number' },
{ regex: '[!@#$%^&*]', message: 'At least one special character' },
];
Uso:
baseFieldsConfigs: {
inputConfigs: {
eInputModeField: EInputModeField.PASSWORD,
regexRules: RULES_REGEX_PASSOWORD_FORM_CONFIGS,
},
}
📐 Config Base Default (form-base-configs-default.ts)
import {
Base_Model_4_Initial_Model_Form_Defaults_Fields_Configs,
IDynamicFormContainerConfig
} from '@Rodrigos-dev/ars-dynamic-form';
// Model 4 como default global
export const defaultAllFieldsConfigsUse =
Base_Model_4_Initial_Model_Form_Defaults_Fields_Configs.defaultAllFieldsConfigs;
export const itemContainerFieldsConfigUse =
Base_Model_4_Initial_Model_Form_Defaults_Fields_Configs.itemContainerFieldsConfigs;
export const FORM_CONTAINER_CONFIG_DEFAULT: IDynamicFormContainerConfig = {
paddingRightForm: '0',
paddingLeftForm: '0',
paddingBottomForm: '0',
paddingTopForm: '0',
maxWidthForm: '100%',
gapFieldsForm: '5px',
};
📋 Checklist ao Criar Formulário
- Criar pasta
forms-configs/na page - Criar arquivo
[nome]-form-config-fields.ts - Exportar função que retorna
IDynamicFormFieldsConfig[] - Importar
defaultAllFieldsConfigsUseeitemContainerFieldsConfigUse - Configurar
validationscom validators Angular - Usar
ValidatorDocumentsAndMorepara documentos (CPF, CNPJ, etc.) - Configurar
baseFieldsConfigsconforme tipo do campo - Adicionar
<mb-dynamic-form>no template - Implementar
onFormChange()elistenFormValidStatus() - Configurar
mbFormStorageNamese necessário - Configurar
formContainerConfigse necessário
⚠️ Gotchas Conhecidos
- Form é destruído e recriado para rehidratação - usar
setTimeoutcom flag valueEqualWithFieldFormNamecompara valores automaticamenteregexRulesrenderiza checklist visual - lib controla validade@ViewChildpode ser necessário para acesso direto ao FormGroupeffect()do Angular pode ser usado para rehidratação reativa
Última atualização: 2026-08-13 Projeto: basic-initial-core-full (Angular 20 + Ionic 8)