zk-mvvm-viewmodel-pattern

star 0

Implementa padrão MVVM no ZK Framework usando @ViewModel, @Command, @Init, @NotifyChange e data binding. Use quando: estiver criando novas UIs ZK 8+, modernizando código legado MVC, ou precisando de ViewModels testáveis sem dependência de componentes UI.

KrystianYCSilva By KrystianYCSilva schedule Updated 2/21/2026

name: zk-mvvm-viewmodel-pattern description: | Implementa padrão MVVM no ZK Framework usando @ViewModel, @Command, @Init, @NotifyChange e data binding. Use quando: estiver criando novas UIs ZK 8+, modernizando código legado MVC, ou precisando de ViewModels testáveis sem dependência de componentes UI.

ZK MVVM ViewModel Pattern

Padrão arquitetural MVVM (Model-View-ViewModel) do ZK Framework que separa lógica de apresentação da UI através de data binding automático. Reduz ~40% do código boilerplate comparado ao MVC com Composers.

Como estruturar um ViewModel

Crie uma classe POJO anotada com @ViewModel (opcional, apenas documentação). O ViewModel deve:

  1. Ter propriedades privadas com getters/setters para dados bound na view
  2. Usar @Init em método de inicialização (executado após construtor)
  3. Usar @Command em métodos acionados por eventos da view
  4. Usar @NotifyChange("prop") para notificar mudanças em propriedades específicas
@ViewModel  // Opcional - apenas documentação
public class FuncionariosViewModel {
    
    private List<Funcionario> funcionarios;
    private Funcionario selecionado;
    
    @Init
    public void init() {
        carregarFuncionarios();
    }
    
    @Command
    @NotifyChange("funcionarios")
    public void carregarFuncionarios() {
        this.funcionarios = service.buscarTodos();
    }
}

Como usar annotations de binding

@Init - Inicialização

Método anotado com @Init é executado automaticamente após o construtor. Use para carregar dados iniciais:

@Init
public void init(@BindingParam("id") Long id) {
    if (id != null) {
        this.entity = service.buscarPorId(id);
    }
}

@Command - Ações da UI

Métodos anotados com @Command são invocados por eventos na view (cliques, submits):

@Command
@NotifyChange({"funcionarios", "mensagem"})
public void salvar() {
    service.salvar(funcionario);
    this.mensagem = "Salvo com sucesso!";
    carregarFuncionarios();
}

@NotifyChange - Notificação de mudanças

Informe quais propriedades foram alteradas para atualizar a UI automaticamente:

// Notifica uma propriedade
@NotifyChange("selecionado")

// Notifica múltiplas propriedades
@NotifyChange({"funcionario", "mensagem", "erros"})

// Notifica tudo (menos eficiente)
@NotifyChange("*")

Como fazer binding na view ZUL

Use expressões @bind, @load, @save, e @command no arquivo .zul:

<window viewModel="br.com.zkminsamples.viewmodel.FuncionariosViewModel">
    
    <!-- Data binding bidirecional -->
    <textbox value="@bind(vm.selecionado.nome)" />
    
    <!-- Data binding unidirecional (leitura) -->
    <label value="@load(vm.usuario.nome)" />
    
    <!-- Invocar command -->
    <button label="Salvar" onClick="@command('salvar')" />
    
    <!-- Binding com parâmetros -->
    <button onClick="@command('editar', item=vm.selecionado)" />
    
    <!-- Template para listas -->
    <listbox model="@load(vm.funcionarios)">
        <template name="model">
            <listitem>
                <listcell label="@bind(each.nome)" />
                <listcell label="@bind(each.email)" />
            </listitem>
        </template>
    </listbox>
</window>

Como passar parâmetros para Commands

Use @BindingParam no método e passe parâmetros na view:

ViewModel:

@Command
public void editar(@BindingParam("item") Funcionario item) {
    this.selecionado = item;
}

View ZUL:

<button label="Editar" 
        onClick="@command('editar', item=each)" />

Como validar dados no ViewModel

Implemente validação manual nos commands ou use Bean Validation:

@Command
@NotifyChange("erros")
public void salvar() {
    this.erros = new HashMap<>();
    
    if (funcionario.getNome() == null || funcionario.getNome().isEmpty()) {
        erros.put("nome", "Nome é obrigatório");
    }
    
    if (!erros.isEmpty()) {
        return;  // Não salva se houver erros
    }
    
    service.salvar(funcionario);
}

Na view:

<textbox value="@bind(vm.funcionario.nome)" />
<label value="@load(vm.erros['nome'])" style="color: red" />

Como integrar com Spring

Em projetos Spring Boot, injete services via construtor:

@ViewModel
@Service
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)  // Importante!
public class UsuariosViewModel {
    
    private final UsuarioService service;
    
    @Autowired
    public UsuariosViewModel(UsuarioService service) {
        this.service = service;
    }
    
    @Init
    public void init() {
        // Service já está injetado
    }
}

Configuração ZK + Spring (ZkAutoConfiguration.java):

@Bean
public ServletRegistrationBean<DHtmlLayoutServlet> zkLayoutServlet(
        ApplicationContext applicationContext) {
    
    DHtmlLayoutServlet servlet = new DHtmlLayoutServlet();
    servlet.setWebAppContext(applicationContext);  // Habilita DI
    
    return new ServletRegistrationBean<>(servlet, "*.zul");
}

Como acessar Security Context

Para obter usuário logado e permissões:

@Init
public void init() {
    Authentication auth = SecurityContextHolder.getContext().getAuthentication();
    
    if (auth != null && auth.isAuthenticated()) {
        this.usuarioLogado = auth.getName();
        this.per missoes = auth.getAuthorities().stream()
            .map(GrantedAuthority::getAuthority)
            .collect(Collectors.toList());
    }
}

public boolean podeEditar() {
    return per missoes.contains("ROLE_ADMIN");
}

Na view:

<button label="Excluir" 
        visible="@load(vm.podeEditar())"
        onClick="@command('excluir')" />

Referências

Install via CLI
npx skills add https://github.com/KrystianYCSilva/zk-min-samples --skill zk-mvvm-viewmodel-pattern
Repository Details
star Stars 0
call_split Forks 0
navigation Branch main
article Path SKILL.md
More from Creator
KrystianYCSilva
KrystianYCSilva Explore all skills →