iberck
3/3/2017 - 8:52 PM

Grails commands databinding alternativa 6

Grails commands databinding alternativa 6

<%@ taglib prefix="datatables" uri="http://github.com/dandelion/datatables" %>

<!DOCTYPE html>
<html>
<head>
    <meta name="layout" content="adminlte">
    <title>Configuración</title>

    <t:assetsInfoBox/>
    <t:assetsOnOff/>

</head>

<body>

%{--BodyHeader--}%
<content tag="header">
    <g:render template="/layouts/active_items" model="[menu: '1', submenu: pageConfig.membresia ? '1.1' : '1.2']"/>

    <h2 class="page-title">
        <g:if test="${pageConfig.membresia}">Edición de la membresía ${pageConfig.membresia.folio}</g:if><g:else>Nueva membresía</g:else>
    </h2>
    <t:breadcrumbs
            data="${['menu.membresias.label': createLink(action: 'listMembresias'), 'Lista de membresías': createLink(action: 'listMembresias')]}"/>
</content>

%{--Datos del contrato--}%
<div class="row">
    <div class="col-sm-2">
        <g:render template="submenuleft" model="[item7: 'active', membresiaInstance:pageConfig.membresia]"/>
    </div>

    <div class="col-sm-10">
        <div class="box box-default">
            <div class="box-header with-border">
                <h3 class="box-title">Datos del contrato</h3>
            </div>

            <div class="box-body">
                <g:if test="${pageConfig.membresia}">
                    <t:membresiaDashboard membresia="${pageConfig.membresia}"/>
                </g:if>
                <g:else>
                    <div class="box-body">
                        <t:callout type="warning" title="Advertencia"
                                   text="No puede ingresar a esta sección hasta no capturar la carátula de la membresía."/>
                    </div>
                </g:else>
            </div>
        </div>
    </div>
</div>

%{--Datos de acceso--}%
<div class="row">
    <div class="col-lg-2 col-md-3">
    </div>

    <div class="col-lg-10 col-md-9">
        <div class="box box-primary">

            <div class="box-header with-border">
                <h3 class="box-title">Datos de acceso</h3>
            </div>

            <g:form method="post" class="form-horizontal" role="form" action="updatePanelAcceso">
                <div class="box-body">
                    <g:render template="panel_config_acceso" model="[panelAcceso: pageConfig.getPanelAcceso()]"/>
                </div>

                <div class="box-footer">

                <t:onceSubmit name="submit3" class="btn btn-default pull-right">
                    <i class="fa fa-save fa-2x"></i> </br>
                    <g:message code="default.button.save.label"/>
                </t:onceSubmit>

                <g:link controller="membresia" action="listMembresias" class="btn btn-default"><i
                        class="fa fa-remove fa-2x"></i> </br>
                    <g:message code="default.cancel.label"/>
                </g:link>

                </div>
            </g:form>
        </div>
    </div>
</div>

%{--Parámetros Conekta--}%
<div class="row">
    <div class="col-lg-2 col-md-3">
    </div>

    <div class="col-lg-10 col-md-9">

        <div class="box box-primary">

            <div class="box-header with-border">
                <h3 class="box-title">Parámetros conekta</h3>
            </div>

            <g:form method="post" class="form-horizontal" role="form" action="updatePanelConekta">
                <div class="box-body">
                    <g:render template="panel_config_conekta" model="[panelConekta: pageConfig.getPanelConekta()]"/>
                </div>

                <div class="box-footer">
                <t:onceSubmit name="submit3" class="btn btn-default pull-right">
                    <i class="fa fa-save fa-2x"></i> </br>
                    <g:message code="default.button.save.label"/>
                </t:onceSubmit>

                <g:link controller="membresia" action="listMembresias" class="btn btn-default"><i
                        class="fa fa-remove fa-2x"></i> </br>
                    <g:message code="default.cancel.label"/>
                </g:link>
                </div>
            </g:form>
        </div>
    </div>
</div>

%{--Parámetros Generales--}%
<div class="row">
    <div class="col-lg-2 col-md-3">
    </div>

    <div class="col-lg-10 col-md-9">

        <div class="box box-primary">

            <div class="box-header with-border">
                <h3 class="box-title">Parámetros generales</h3>
            </div>

            <g:form method="post" class="form-horizontal" role="form" action="updatePanelGral">
                <div class="box-body">
                    <g:render template="panel_config_gral" model="[panelGral: pageConfig.getPanelGral()]"/>
                </div>

                <div class="box-footer">
                <t:onceSubmit name="submit3" class="btn btn-default pull-right">
                    <i class="fa fa-save fa-2x"></i> </br>
                    <g:message code="default.button.save.label"/>
                </t:onceSubmit>

                <g:link controller="membresia" action="listMembresias" class="btn btn-default"><i
                        class="fa fa-remove fa-2x"></i> </br>
                    <g:message code="default.cancel.label"/>
                </g:link>
                </div>
            </g:form>
        </div>
    </div>
</div>

</body>
</html>
@GrailsCompileStatic
class PageConfig extends PageWithPanels<Membresia> {

    Membresia membresia

    PageConfig(Membresia membresia) {
        this.membresia = membresia
        buildPanels(membresia)
    }

    void addPanels() {
        panels.add(new PanelAcceso())
        panels.add(new PanelConekta())
        panels.add(new PanelGral())
    }

    PanelAcceso getPanelAcceso() {
        return getPanel("PANEL_ACCESO") as PanelAcceso
    }

    PanelConekta getPanelConekta() {
        return getPanel("PANEL_CONEKTA") as PanelConekta
    }

    PanelGral getPanelGral() {
        return getPanel("PANEL_GRAL") as PanelGral
    }
}
@GrailsCompileStatic
@Validateable
class PanelConekta implements Panel<Membresia> {

    Membresia membresia
    String idCliente
    AccesoService accesoService

    @Override
    String getKey() {
        return "PANEL_CONEKTA"
    }

    @Override
    Membresia getInstance() {
        return membresia
    }

    @Override
    void build(Membresia membresia) {
        this.membresia = membresia
        idCliente = membresia.conektaCustomerId
    }

    @Override
    void savePanel() {
        accesoService.saveOrUpdatePanel(this)
    }

    @Override
    boolean validateHasErrors() {
        return hasErrors()
    }

    static constraints = {
        idCliente nullable: true, blank: true
    }
}
@GrailsCompileStatic
@Validateable
class PanelAcceso implements Panel<Membresia> {

    Membresia membresia
    String username
    String password
    AccesoService accesoService

    @Override
    String getKey() {
        return "PANEL_ACCESO"
    }

    @Override
    Membresia getInstance() {
        return membresia
    }

    @Override
    void build(Membresia membresia) {
        this.membresia = membresia
        username = membresia.accesoSocio?.username
        password = membresia.accesoSocio?.password
    }

    @Override
    void savePanel() {
        accesoService.saveOrUpdatePanel(this)
    }

    @Override
    boolean validateHasErrors() {
        return hasErrors()
    }

    boolean isUsuarioRegistrado() {
        return membresia.accesoSocio != null
    }

    static constraints = {
        username blank: false, validator: {String value, PanelAcceso _this ->
            if (!_this.accesoService.isUserNameAvailable(value)) {
                return "acceso.usuarioRepetido"
            }
        }
        password blank: false, minSize: 6
    }
}
@GrailsCompileStatic
abstract class PageWithPanels<T> {

    List<Panel> panels = new ArrayList()

    protected void buildPanels(T instance) {
        addPanels()
        for (def panel : panels) {
            panel.build(instance)
        }
    }

    protected Panel<T> getPanel(String id) {
        for (int i = 0; i < panels.size(); i++) {
            if (id == panels[i].key) {
                return panels[i]
            }
        }
    }

    void replace(Panel<T> panel) {
        for (int i = 0; i < panels.size(); i++) {
            if (panel.key == panels[i].key) {
                panels[i] = panel
                break
            }
        }
    }

    abstract void addPanels()
}
@GrailsCompileStatic
class PageConfig extends PageWithPanels<Membresia> {

    Membresia membresia

    PageConfig(Membresia membresia) {
        this.membresia = membresia
        buildPanels(membresia)
    }
  
    // Paneles de la página, cuando se agregue un nuevo panel habrá que agregarlo aquí.
    void addPanels() {
        panels.add(new PanelAcceso())
        panels.add(new PanelConekta())
        panels.add(new PanelGral())
    }

    PanelAcceso getPanelAcceso() {
        return getPanel("PANEL_ACCESO")
    }

    PanelConekta getPanelConekta() {
        return getPanel("PANEL_CONEKTA")
    }

    PanelConekta getPanelGral() {
        return getPanel("PANEL_GRAL")
    }
}
class MembresiaController {
  
    // Envía a la página de configuración
    def config(Membresia membresiaInstance) {
        PageConfig pageConfig = new PageConfig(membresiaInstance)
        render view: "config", model: [pageConfig: pageConfig]
    }

    def updatePanelAcceso(PanelAcceso panelAcceso) {
        updatePanel(panelAcceso)
    }

    def updatePanelConekta(PanelConekta panelConekta) {
        updatePanel(panelConekta)
    }

    def updatePanelGral(PanelGral panelGral) {
        updatePanel(panelGral)
    }

    private def updatePanel(Panel<Membresia> panel) {
        PageConfig pageConfig = new PageConfig(panel.instance)
        pageConfig.replace(panel)
        if (panel.validateHasErrors()) {
            render(view: "config", model: [pageConfig: pageConfig])
            return
        }
        panel.savePanel()
        flash.success = "Se guardaron correctamente los parámetros del panel"
        redirect action: "config", params: [id: panel.instance.id]
    }
}

Alternativa 6

Estas alternativas son ideales para una página que tiene N paneles y cada uno ejecuta una lógica de validación distinta.

La alternativa 5 tiene el problema que cuando se agrega un nuevo panel, hay que modificar todas las acciones para agregar el nuevo panel rompiendo el principio open/close. Esta alternativa no repite código como en la alternativa 5, además que respeta el principio open/close.

Esta alternativa se basa en la idea de tener 1 command object que represente cada panel, y una clase PageWithPanels que representa la página con todos los paneles.

Ventajas

  • Las mismas que la alternativa 5
  • El nombre de las propiedades del domain class y del command object no tienen forzosamente que coincidir, entonces si se hace refactor del nombre de la propiedad en el command o en el domain class, no importa y seguirá funcionando la página.
@GrailsCompileStatic
interface Panel<T> {

    // Id que representa al panel
    String getKey()

    // el panel se construye en base al objeto instance
    void build(T instance)

    // Obtiene la instancia con la que se construyen todos los paneles
    T getInstance()

    // se guarda el panel
    void savePanel()

    boolean validateHasErrors()
}