New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details →
Socket
Book a DemoSign in
Socket

@praxisui/manual-form

Package Overview
Dependencies
Maintainers
1
Versions
65
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@praxisui/manual-form

Manual form toolkit for Praxis UI: container, instance factory and editor bridge for @praxisui/* fields.

latest
Source
npmnpm
Version
1.0.0-beta.28
Version published
Maintainers
1
Created
Source

Manual Form Toolkit

🔰 Exemplos / Quickstart

Para ver esta biblioteca em funcionamento em uma aplicação completa, utilize o projeto de exemplo (Quickstart):

  • Repositório: https://github.com/codexrodrigues/praxis-ui-quickstart
  • O Quickstart demonstra a integração das bibliotecas @praxisui/* em um app Angular, incluindo instalação, configuração e uso em telas reais.

Instalação

npm install @praxisui/manual-form

Peers necessários (instale no app host):

  • @angular/core ^20.1.0, @angular/common ^20.1.0
  • @praxisui/core (ConfigStorage, GenericCrudService, ícones)
  • @praxisui/dynamic-fields (componentes de campo pdx-*)
  • @praxisui/settings-panel (integração com painel de configurações)
  • @praxisui/metadata-editor (edição visual de campos, opcional)

Problema

Montar formulários Praxis manualmente (usando <pdx-*>) exigia montar FormConfig, FormGroup, persistência e metadados manualmente. Isso gerava atrito para hosts e dificultava a evolução.

Solução

Criamos a biblioteca @praxisui/manual-form com:

  • ManualFormComponent: container que detecta campos, infere FieldMetadata, cria o FormGroup, mantém hot update e persiste via ManualFormInstance. Aceita inputs formId, formTitle, actions, enableAutoSave etc.
  • ManualFormInstanceFactory: runtime que cuida da persistência (createManualFormSeed, saveDraft, resetToSeed).
  • ManualFormHeader/Actions: componentes auxiliares para título/descritivo e ações (submit/cancel/reset/custom).
  • ManualFieldMetadataBridgeService: integra com o @praxisui/metadata-editor, aplica patches e garante persistência via ManualFormInstance.

O exemplo /cargos/manual-form demonstra uso declarativo:

<praxis-manual-form formId="cargo">
  <pdx-text-input formControlName="nome" label="Nome" required></pdx-text-input>
  <pdx-material-currency formControlName="salario" label="Salário"></pdx-material-currency>
</praxis-manual-form>

Editor de metadados integrado

Use o ManualFieldMetadataBridgeService para abrir o editor visual a partir de qualquer campo detectado pelo container. O serviço carrega o módulo sob demanda, aplica o patch retornado e chama saveDraft() para persistir as alterações no runtime.

@Component({
  /* ... */
})
export class CargoManualFormComponent {
  @ViewChild(ManualFormComponent) manualForm?: ManualFormComponent;

  constructor(private readonly metadataBridge: ManualFieldMetadataBridgeService) {}

  openFieldEditor(fieldName: string): void {
    const instance = this.manualForm?.instance;
    if (!instance) {
      return;
    }
    void this.metadataBridge.openEditor(instance, fieldName);
  }
}

Motivações

  • DX coerente para formulários manuais (sem JSON).
  • Compatibilidade com o ecossistema (metadados, persistência, metadata editor).
  • Facilita adoção por hosts sem precisar do builder completo.

Estado atual

  • Container autodetecta campos (selector/constructor → FieldControlType), infere label, validators.required.
  • Persistência automática (ManualFormInstance/ConfigStorage).
  • Header/Ações padrões com FormActionsLayout (normalizado por DEFAULT_ACTIONS).
  • Seeds usam ensureIds.
  • Autosave opcional com debounce configurável (enableAutoSave + autoSaveDebounceMs).

Próximos passos

  • Enriquecer inferência (opções, limites numéricos, etc.).
  • Permitir layout personalizado via @Input() sections ou layoutConfig.
  • Expandir testes unitários (detecção de campos, persistência, normalização de ações, nested paths).
  • Documentar API pública com exemplos (README + docs).
  • Adicionar feedbacks visuais pós-edição (notificações, toasts).

Notas para evolução

  • Continue a trabalhar em projects/praxis-manual-form e atualize demos (ex. /src/app/features/cargo-manual-form).
  • Ao rodar ng-packagr, confirme que libs referenciadas (@praxisui/core, @praxisui/dynamic-fields) estão construídas ou apontam para dist/.
  • Respeite contratos de FieldMetadata e use helpers (ensureIds, deepClone).
  • Atenção a inputs obrigatórios (alguns componentes exigem metadata, readonlyMode, etc.).
  • Prefira inline styles para evitar warnings de arquivo ausente.

Essa base permite que hosts manualmente criem formulários com componentes Praxis, mantendo compatibilidade com o restante do ecossistema (persistência, metadados e editor).

Padrão corporativo recomendado

  • Host tipado: o host deve criar um FormGroup tipado e passá‑lo via [formGroup]. O container adota esse grupo, aplica metadados/validators e evita interceptações internas. Isso habilita o Angular Language Service a validar formControlName e melhora a DX.
  • Modo dinâmico: se nenhum [formGroup] for informado, o container cria e gerencia um FormGroup interno (útil para PoCs), sem garantia de validação estática de nomes.

Configurações avançadas

  • @Input() usePathNames: boolean (padrão: false)
    • Quando habilitado, usa FormControlName.path (segmentos unidos por .) como FieldMetadata.name. Requer suporte a grupos aninhados — já implementado em DynamicFormService.
  • Mapeamentos de inferência via DI
    • MANUAL_FORM_SELECTOR_TO_CONTROL_TYPE e MANUAL_FORM_CONSTRUCTOR_TO_CONTROL_TYPE permitem sobrescrever o mapa padrão de seletores/constructors → FieldControlType.
  • Autosave
    • @Input() enableAutoSave: boolean (padrão: true) e @Input() autoSaveDebounceMs: number (padrão: 800).

SSR (ConfigStorage sem localStorage)

Em SSR, localStorage não existe. Como praxis-manual-form apenas depende do token CONFIG_STORAGE, você pode prover uma implementação segura no servidor (ex.: in‑memory) e manter LocalStorageConfigService no browser.

Exemplo (Angular com app.config.server.ts):

// app.config.server.ts
import { ApplicationConfig, PLATFORM_ID, inject, isPlatformServer } from '@angular/core';
import { CONFIG_STORAGE, LocalStorageConfigService, type ConfigStorage } from '@praxisui/core';

class MemoryConfigStorage implements ConfigStorage {
  private readonly map = new Map<string, any>();
  loadConfig<T>(key: string): T | null { return (this.map.get(key) as T) ?? null; }
  saveConfig<T>(key: string, config: T): void { this.map.set(key, config); }
  clearConfig(key: string): void { this.map.delete(key); }
}

export const appConfig: ApplicationConfig = {
  providers: [
    {
      provide: CONFIG_STORAGE,
      useFactory: () => isPlatformServer(inject(PLATFORM_ID))
        ? new MemoryConfigStorage()
        : inject(LocalStorageConfigService),
    },
  ],
};

Alternativas no servidor: usar cookies, cache distribuído (ex.: Redis) ou persistir por sessão do usuário. Basta implementar a interface ConfigStorage.

Exemplo (nested form)

<form [formGroup]="form">
  <praxis-manual-form formId="nested" [usePathNames]="true">
    <div formGroupName="endereco">
      <pdx-text-input formControlName="logradouro" label="Logradouro"></pdx-text-input>
      <pdx-text-input formControlName="numero" label="Número"></pdx-text-input>
    </div>
  </praxis-manual-form>
</form>

Com usePathNames=true, os metadados serão gerados com nomes endereco.logradouro e endereco.numero, mantendo o FormGroup aninhado do host.

Quando usar (e quando não)

Use esta biblioteca quando:

  • Você quer controlar o layout via HTML, reaproveitando os componentes Praxis.
  • Prefere começar simples e evoluir metadados gradualmente (inferência + editor visual opcional).
  • Precisa integrar com persistência de rascunhos e editar propriedades de campos em runtime.

Prefira o formulário totalmente dinâmico (JSON com @praxisui/dynamic-form) quando:

  • O layout deve ser 100% dirigido por metadados/JSON (sem HTML manual).
  • É necessário trocar telas apenas alterando seeds/backends.
  • Precisa de recursos avançados do motor dinâmico (regras, seções condicionais, layouts declarativos complexos).

Guia rápido (host tipado)

// component.ts
@Component({ /* … */ })
export class MinhaPagina {
  private readonly fb = inject(FormBuilder);
  readonly form = this.fb.group({
    nome: this.fb.control('', { nonNullable: true, validators: [Validators.required] }),
    endereco: this.fb.group({ logradouro: this.fb.control('', { nonNullable: true }) }),
  });
}
<praxis-manual-form
  [formGroup]="form"
  formId="meu-form"
  [usePathNames]="true"
  formTitle="Cadastro"
  (submitted)="onSubmit($event)"
>
  <pdx-text-input formControlName="nome" label="Nome"></pdx-text-input>
  <div formGroupName="endereco">
    <pdx-text-input formControlName="logradouro" label="Logradouro"></pdx-text-input>
  </div>
</praxis-manual-form>

Observações importantes:

  • Aplique [formGroup] diretamente no seletor <praxis-manual-form> (no host).
  • Evite envolver o componente com um <form [formGroup]> externo; não há necessidade e pode causar forms aninhados.

Modo dinâmico (sem host tipado):

  • Quando nenhum [formGroup] é informado no host, o container renderiza internamente um <form [formGroup]="formGroup"> para atender o Angular Forms e evitar o erro NG01050.
  • Isso dispensa qualquer “shim” externo para os exemplos dinâmicos.

API do ManualFormComponent (resumo)

  • Inputs (sinais):

    • formId: string (obrigatório)
    • formTitle?: string, formDescription?: string
    • actions?: FormActionsLayout | null
    • showHeader = true, showActions = true
    • enableAutoSave = true, autoSaveDebounceMs = 800
    • editModeEnabled = false (habilita ações de customização — ex.: abrir editor por duplo clique)
    • persistenceOptions?: { namespace?, tenantId?, profileId?, locale? }
    • usePathNames = false (usa FormControlName.path como nome do campo)
  • Outputs (sinais):

    • submitted({ value, instance }), saved(instance), reset(instance)
    • metadataChange(FormConfig)
  • Métodos públicos:

    • tryOpenFieldEditor(fieldName: string): tenta abrir o editor do campo, respeitando editModeEnabled (no‑op quando false).

Header e Actions

  • <praxis-manual-form-header>: recebe instance, title, description, saveLabel, resetLabel e emite save/reset.
  • Quando editModeEnabled está ativo no container, o header exibe um botão “Editar formulário” que emite editForm; o container trata esse evento chamando openFormEditor().
  • <praxis-manual-form-actions>: recebe actions: FormActionsLayout, emite actionClick e aceita trackByFn opcional para listas grandes.

Editor de Metadados (visual)

@ViewChild(ManualFormComponent) manual?: ManualFormComponent;
constructor(private readonly bridge: ManualFieldMetadataBridgeService) {}
openEditor(fieldName: string) {
  const inst = this.manual?.instance; if (!inst) return;
  this.bridge.openEditor(inst, fieldName).catch(console.error);
}

Integração com Settings Panel: o ManualFormComponent utiliza SettingsPanelService para abrir o editor do formulário (lista de campos e flags) quando editModeEnabled está ativo. É possível abrir programaticamente via manualForm.openFormEditor().

Duplo clique com modo de edição

Para espelhar o comportamento das demais libs (permitir edição apenas quando o modo de edição está ativo), use o input editModeEnabled no container e a diretiva pdxManualEdit nos campos:

<praxis-manual-form formId="cargo" [editModeEnabled]="custom.enabled()">
  <pdx-text-input formControlName="nome" label="Nome" pdxManualEdit="nome"></pdx-text-input>
  <pdx-material-currency formControlName="salario" label="Salário" pdxManualEdit="salario"></pdx-material-currency>
</praxis-manual-form>

A diretiva chama manualForm.tryOpenFieldEditor(fieldName) e respeita editModeEnabled, de modo que o duplo clique só abre o editor quando a edição está ligada.

Editor do Formulário (lista de campos)

O ManualFormComponent expõe openFormEditor() para abrir um editor simples do formulário que lista todos os campos e permite alternar: visibilidade (mostrar/ocultar), obrigatório, somente leitura e desabilitado — facilitando encontrar campos ocultos e reexibi‑los, além de ajustes rápidos. O editor respeita editModeEnabled e usa o SettingsPanelService.

Exemplo de uso no host:

<button *ngIf="custom.enabled()" type="button" (click)="manualForm?.openFormEditor()">Editar formulário</button>
<praxis-manual-form #manualForm [formGroup]="form" formId="'cargo'" [editModeEnabled]="custom.enabled()">
  <!-- campos -->
</praxis-manual-form>

Extensibilidade por DI

  • Customize a inferência de FieldControlType via tokens:
    • MANUAL_FORM_SELECTOR_TO_CONTROL_TYPE
    • MANUAL_FORM_CONSTRUCTOR_TO_CONTROL_TYPE

ManualFieldDirective

<ng-container *praxisManualField="'nome'; praxisManualFieldInstance: manual.instance as ctx">
  Label atual: {{ ctx?.label }}
</ng-container>

ManualFieldEditorOnDblclickDirective (pdxManualEdit)

  • Usa o duplo clique para abrir o editor do campo, respeitando editModeEnabled do ManualFormComponent.
  • Apenas funciona quando aplicada a elementos dentro de <praxis-manual-form>.
<praxis-manual-form formId="cargo" [editModeEnabled]="custom.enabled()">
  <pdx-text-input formControlName="nome" label="Nome" pdxManualEdit="nome"></pdx-text-input>
</praxis-manual-form>

Boas práticas

  • Preferir host tipado e usePathNames=true para nested forms.
  • Habilitar autosave com debounce adequado.
  • Evitar lógica pesada em templates; delegar inferências ao container.
  • No SSR, sempre prover CONFIG_STORAGE compatível (sem localStorage).

Licença

Apache-2.0 — consulte LICENSE neste pacote ou no repositório raiz.

Limitações

  • Inferência automática é propositalmente minimalista (label, required, tipo básico). Propriedades avançadas exigem metadados explícitos/edição visual.
  • Layout é responsabilidade do host; não há reordenação automática por metadados.
  • Modo dinâmico sem [formGroup] não habilita validação estática de formControlName no IDE.

Comparativo — Manual vs Dinâmico (JSON)

  • Manual: controle máximo do HTML; evolui por metadados incrementais; ótimo para telas estáveis com design específico.
  • Dinâmico: telas dirigidas por JSON com alto poder de reconfiguração; ideal para múltiplas telas e variações rápidas sem alterar HTML.

Keywords

angular

FAQs

Package last updated on 07 Nov 2025

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts