Grails webflow with ajax
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>
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
.
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>
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>
Cuando se renderiza un template como respuesta de una petición ajax, existen las siguientes reglas dentro del template:
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']}
.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>
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)