iberck
12/1/2016 - 5:14 AM

Grails webflow with ajax

Grails webflow with ajax

Grails webflow ajax (g:formRemote)

Envía un formulario a través de ajax a través de la etiqueta g:formRemote como un evento del webflow.

Controlador:

class TestController {

    def index() {
    }

    def wizardFlow = {
        busqueda {
            on("searchAjax") {
                flow.automovil = "bmw m5"
                def nombre = params.nombre
                render(template: "/test/wizard/test_template", model: [nombre: nombre, automovil: flow.automovil])
            }.to "busqueda"
        }
    }
}

busqueda.gsp:

<html>
<head xmlns="http://www.w3.org/1999/html" xmlns="http://www.w3.org/1999/html">
    <meta name="layout" content="adminlte">
    <script>
        function onSuccessRemoteCall(data) {
            $("#wizardDiv").html(data);
        }
    </script>
</head>


<body>

<g:formRemote name="form1" url="[action: 'wizard', event:'searchAjax', params:[ajaxSource: true, execution: request.flowExecutionKey]]" onSuccess="onSuccessRemoteCall(data);" >
    Nombre: <g:textField name="nombre"/>
    <g:actionSubmit value="enviar" action="formSubmit"/>
</g:formRemote>
<div id="wizardDiv"></div>

</body>

</html>

_test_template.gsp:

<body>

<b>Hola negrita de nombre ${nombre}</b>
<p>Automovil: ${automovil}</p>

</body>

Grails webflow ajax (g:remoteLink)

Lanza una petición ajax a través de la etiqueta g:remoteLink como un evento del webflow.

Controller:

class TestController {

    def index() {
        redirect(action: "wizard")
    }

    def wizardFlow = {
        busqueda {
            on("searchAjax") {
                flow.msg = "hola"
                render( template: "/test/wizard/test_template", model: [msg: flow.msg] )
            }.to "busqueda"
        }
    }
}

busqueda.gsp

<html>
<head xmlns="http://www.w3.org/1999/html" xmlns="http://www.w3.org/1999/html">
    <meta name="layout" content="adminlte">
    <script>
        function onSuccessRemoteCall(data) {
            $("#wizardDiv").html(data);
        }
    </script>
</head>


<body>

<g:remoteLink onSuccess="onSuccessRemoteCall(data);" action="wizard" event="searchAjax"
              params="[ajaxSource: true, execution: request.flowExecutionKey]">Ajax link</g:remoteLink>
<div id="wizardDiv"></div>

</body>

</html>

_test_template.gsp:

<body>
<b>Hola negrita, ${msg}</b>
</body>

Note que en remoteLink se envía el atributo event el cual indica el evento lanzado en el webflow, si no se utiliza este tag hay que enviar el parámetro _eventId.

Grails webflow jquery/ajax (form)

Envía un formulario como evento de un webflow directamente con jquery.

class TestController {

    def index() {
        redirect(action: "wizard")
    }

    def wizardFlow = {
        busqueda {
            on("searchAjax") {
                flow.name = params.nombre
                render(template: "/taglibTemplates/test_template", model: [nombre: flow.name])
            }.to "busqueda"
            on("info").to("info")
        }
        info {
            on("back").to "busqueda"
            on("finalizar").to "finish"
        }
        finish {
            redirect(action: "index")
        }
    }
}
<html>
<head xmlns="http://www.w3.org/1999/html" xmlns="http://www.w3.org/1999/html">
    <meta name="layout" content="adminlte">
    <script>
        function submitForm(form) {
            $.ajax({
                type: 'POST',
                url: '${raw(g.createLink(event: 'searchAjax', params: [ajaxSource: true, execution: request.flowExecutionKey]))}',
                data: $(form).serialize(),
                success: function (data,textStatus) {
                    $('#wizardDiv').append(data);
                },
                error: function (jqXHR, textStatus, errorThrown) {
                }
            });
        }
    </script>
</head>

<body>

<g:form onsubmit="submitForm(this); return false;" name="form1">
    Nombre: <g:textField name="nombre"/>
    <input type="submit" name="_action_enviar" value="enviar">
</g:form>

<div id="wizardDiv"></div>

</body>

</html>

_test_template.gsp:

<b>Hola negrita de nombre ${nombre}</b>

info.gsp:

<body>

<h1>Info</h1>
Nombre:${name}

<g:link event="back" params="[execution:flowExecutionKey]">Back</g:link>
<br/>
<g:link event="finalizar" params="[execution:flowExecutionKey]">Finish</g:link>

</body>

jquery/ajax onclick que lanza evento webflow (a href)

El evento javascript lanza una petición ajax directamente con jquery, este método a diferencia de g:remoteFunction permite enviar más de 1 parámetro javascript al controlador.

class TestController {

    def index() {
        redirect(action: "wizard")
    }

    def wizardFlow = {
        busqueda {
            on("searchAjax") {
                render( template: "/test/wizard/test_template", model: [id: params.id] )
            }.to "busqueda"
        }
    }
}

busqueda.gsp:

<html>
<head xmlns="http://www.w3.org/1999/html" xmlns="http://www.w3.org/1999/html">
    <meta name="layout" content="adminlte">
    <script>
        function enviarAjax() {
            $.ajax({
                type: "POST",
                url: "${raw(g.createLink( action:'wizard', event: 'searchAjax', params: [ajaxSource: true, execution: request.flowExecutionKey]))}",
                data: {id: Math.random()}, // Datos que se envían al controlador/acción
                success: function (data) {
                    // $('#container').append(data); agrega al div
                    $('#container').html(data); // remplaza el contenido del div
                }
            });
        }
    </script>
</head>


<body>

<g:link href="#" onclick="enviarAjax();">Enviar </g:link>
<a href="#" onclick="enviarAjax();">Enviar por jquery/ajax</a>
<div id="container"></div>

</body>

</html>

_test_template.gsp:

<body>
<b>Hola negrita, ${id}</b>
</body>

jquery/ajax _template onchange que lanza evento webflow

Cuando se renderiza un template como respuesta de una petición ajax, existen las siguientes reglas dentro del template:

  • El template NO puede hacer referencia a las variables del flow, es necesario enviar las variables en un modelo.
  • El flowExecutionKey no se obtiene con request.flowExecutionKey/flowExecutionKey, la manera de obtenerlo es enviándolo al modelo del template. TODO: La manera de obte con ${params['execution']}.
  • Es necesario especificar el controlador en los g:createLink

Ejemplo que muestra cómo aplicar las reglas de ajax dentro de un template que lanza eventos webflow:

class TestController {

    def index() {
        redirect(action: "wizard")
    }

    def wizardFlow = {
        busqueda {
            on("searchAjax") {
                render(template: "/test/wizard/test_template", model: [id: params.id, execution: params.execution])
            }.to "busqueda"
            on("changeAjax") {
                def counter = params.counter
                render text: "counter=$counter"
            }.to "busqueda"
        }
    }

busqueda.gsp

<html>
<head xmlns="http://www.w3.org/1999/html" xmlns="http://www.w3.org/1999/html">
    <meta name="layout" content="adminlte">
    <script>
        function ajaxCall() {
            $.ajax({
                type: "POST",
                url: "${raw(g.createLink( action:'wizard', event: "searchAjax", params: [ajaxSource: true, execution: request.flowExecutionKey]))}",
                data: {id: Math.random()}, // Datos que se envían al controlador/acción
                success: function (data) {
                    // $('#container').append(data); agrega al div
                    $('#container').html(data); // remplaza el contenido del div
                }
            });
        }
    </script>
</head>


<body>

<a href="#" onclick="ajaxCall();">Enviar por jquery/ajax</a>
<div id="container"></div>

</body>

</html>

_test_template.gsp

<html>
<head xmlns="http://www.w3.org/1999/html" xmlns="http://www.w3.org/1999/html">
    <meta name="layout" content="adminlte"/>
    <script>
        function ajaxCall(url, value) {
            $.ajax({
                type: "POST",
                url: url,
                data: {counter: value},
                success: function (data) {
                    $("#container2").html(data);
                }
            });
        }
    </script>
</head>

<body>

<input type="number"
       onchange="ajaxCall('${raw(g.createLink(controller: 'test', action:'wizard', event: 'changeAjax', params: [ajaxSource: true, execution: execution]))}', this.value);"/>
<div id="container2"></div>

</body>
</html>

SnapshotNotFoundException

Por defecto si se lanzan más de 30 peticiones ajax, lanza una SnapshotNotFoundException, para evitarlo es necesario agregar el siguiente código en Bootstrap.groovy:

DefaultFlowExecutionRepository defaultFlowExecutionRepository=(DefaultFlowExecutionRepository)Holders.applicationContext.getBean('flowExecutionRepository');
        defaultFlowExecutionRepository.setMaxSnapshots(-1)

http://stackoverflow.com/questions/24300651/web-flow-exception-no-flow-execution-snapshot-could-be-found-with-id-1