Resposta rápida
- O que muda
SetPropertyreescreve, em tempo de execução, uma propriedade que a estrutura leu do dicionário — sem tocar no SX3.- Onde aplicar
- na estrutura de MODELO (
MODEL_FIELD_*) ou na de VIEW (MVC_VIEW_*) — nunca a mesma constante serve para os dois lados. - A armadilha central
- bloquear no modelo com
MODEL_FIELD_WHENfaz oSetValuedo seu próprio código falhar em silêncio; bloquear na view comMVC_VIEW_CANCHANGEtrava só quem digita. - Erro de digitação clássico
MVC_VIEW_ORDERnão existe — a constante éMVC_VIEW_ORDEM, em português.- Onde a regra não alcança
- o que está escrito no
ModelDefde um MVC não vale para a rotina tradicional da mesma tabela, e o inverso também não.
Consulta rápida7 seções
FWFormStruct monta a estrutura de campos lendo o SX3 — é assim que o
ModelDef sabe o tipo, o tamanho e a obrigatoriedade de cada campo sem uma
linha escrita à mão. O problema aparece quando a rotina precisa de uma regra
diferente daquela que está no dicionário: um campo opcional no cadastro
padrão que precisa ser obrigatório nesta tela, uma validação que só faz
sentido para este fluxo, um campo que só pode ser preenchido pelo código e
nunca pelo usuário. Mexer no SX3 resolveria, mas vale para o sistema inteiro
— e é exatamente o que não se quer. SetProperty resolve o problema certo:
reescreve a propriedade só para a estrutura que esta rotina montou, na
memória, sem gravar nada no dicionário. A mesma tabela pode ter regras
diferentes em telas diferentes porque cada ModelDef chama FWFormStruct
de novo e recebe um objeto próprio para mexer.
A forma real, observada no uso
Na prática, a chamada aparece assim — não há uma assinatura publicada, só o padrão que se repete em centenas de lugares:
oStruct:SetProperty(cIdCampo, nPropriedade, xValor)cIdCampo é o nome do campo ("A1_COD") ou o curinga "*", que aplica a
propriedade a todos os campos da estrutura de uma vez. nPropriedade é uma
das constantes de FWMVCDef.ch — de uma família se oStruct veio de
FWFormStruct(1,...), de outra se veio de FWFormStruct(2,...). xValor
é o novo valor: um lógico, um code-block, o que a propriedade esperar.
As constantes do lado do MODELO
São 14, de MODEL_FIELD_TITULO (1) a MODEL_FIELD_VIRTUAL (14). As cinco
que aparecem no dia a dia de quem escreve regra fora do dicionário:
| Constante | Valor |
|---|---|
MODEL_FIELD_VALID | 7 |
MODEL_FIELD_WHEN | 8 |
MODEL_FIELD_OBRIGAT | 10 |
MODEL_FIELD_INIT | 11 |
MODEL_FIELD_NOUPD | 13 |
VALID e WHEN são as mais chamadas das cinco — validação e trava de
digitação são a regra mais comum de se personalizar fora do dicionário.
VALID troca o bloco de validação do campo. WHEN decide se o campo aceita
digitação. OBRIGAT liga ou desliga a obrigatoriedade sem encostar no SX3.
INIT troca o valor inicial. NOUPD bloqueia edição depois de gravado —
diferente de WHEN, que bloqueia sempre.
As constantes do lado da VIEW
A família MVC_VIEW_* tem 17 posições, e estas são as que mais aparecem
depois das estruturais:
| Constante | Valor |
|---|---|
MVC_VIEW_ORDEM | 2 |
MVC_VIEW_LOOKUP | 9 |
MVC_VIEW_CANCHANGE | 10 |
CANCHANGE é, de longe, a mais chamada das três — travar a edição pela
view, sem mexer no modelo, decidindo se o campo aparece editável ou só para
leitura na tela, é o caso mais comum desta seção inteira.
LOOKUP liga ou desliga a lupa de consulta daquele campo específico.
O curinga que aplica a todos os campos
Quando a regra vale para a estrutura inteira, o primeiro argumento vira
"*". Duas formas reais:
oStruct:SetProperty("*", MODEL_FIELD_VALID, {|| lVldModel })
oStruct:SetProperty("*", MVC_VIEW_CANCHANGE, .F.)A primeira troca a validação de todo campo da estrutura de modelo pelo mesmo
bloco. A segunda bloqueia a edição da tela inteira de uma vez — útil na
entidade que a rotina só exibe, como um SA1MASTER de cabeçalho.
A armadilha: bloquear no modelo não é o mesmo que bloquear na view
A distinção é esta: o modelo é o dado, e uma trava nele vale para qualquer
caminho que passe por ali — tela, ExecAuto, evento. A view é a
apresentação; uma trava nela impede o clique do usuário e não afeta o que o
código faz no modelo por baixo. Antes de escrever
SetValue contra um campo que talvez esteja bloqueado, há a saída
defensiva: CanSetValue(cIdForm, cIdField) devolve se aquela gravação é
permitida, e testar antes evita descobrir a trava pelo sintoma. Quando a
gravação precisa acontecer de qualquer jeito, o modelo tem ForceValue.
SetValue respeita a trava; ForceValue, não.
A mensagem de erro sai por SetErrorMessage, nunca por MsgAlert
Quando uma validação de campo falha dentro do modelo, quem avisa o usuário é
sempre SetErrorMessage — nunca MsgAlert nem FWAlertError, porque o MVC
só trata a mensagem que passa por esse método. As nove posições dele, a
armadilha das duas vírgulas iniciais e o exemplo completo de uma
pós-validação estão em
Os eventos de um modelo MVC.
Onde a regra não alcança
Uma propriedade reescrita com SetProperty dentro de um ModelDef vale só
para aquele modelo. A rotina tradicional que atualiza a mesma tabela por
fora — um batch com RecLock direto, uma tela nunca convertida — não
enxerga nada disso: os dois fontes não compartilham estrutura em tempo de
execução. O inverso também vale: trava escrita num ponto de entrada
tradicional não é lida pelo MVC. Quem precisa da mesma regra valendo nos
dois mundos escreve a regra dentro do próprio dicionário, quando ela é
sempre válida, ou numa função comum chamada pelos dois lados, quando depende
do fluxo.
Esta é a última peça da série sobre MVC no Protheus: depois do esqueleto de três funções, do modelo e da view, e do cadastro pai-filho com grid, fica esta ferramenta fina — mudar uma regra sem tocar em nada que o resto do sistema também usa.
Se um campo seu não aceita o valor que o código manda gravar, fale com a gente.
Referências
TDNtdn.totvs.com
TDN — FWFormModelStructTDNtdn.totvs.com
TDN — FWFormModel
Continue por aqui
- Vídeo
ProtheusTutorial
MVC pai e filho: cabeçalho e itens na mesma tela
MVC pai e filho: AddGrid inverte a ordem dos blocos de AddFields, SetRelation liga cabeçalho e item. O que muda quando a tela ganha um grid.
- Vídeo
ProtheusTutorial
Seu primeiro cadastro MVC, do dicionário à tela
Primeiro cadastro MVC do zero: uma tabela nova, os três artigos da série num fonte só, e o que dá errado na primeira compilação.