Utilização de Ajax em Processos
A utilização de Ajax em processos de Workflow requer a utilização de alguns padrões.
O código que será executado pelo Ajax deverá estar contido no método de uma classe (ou seja, não será possível executar funções "soltas").
O arquivo que contém a classe deverá estar localizado na área "code" (que é o mesmo diretório do arquivo shared.php) do processo. O nome deverá seguir o formato: class.ajax.nome_classe.inc.php, por exemplo, supondo que irei chamar, por Ajax, a classe validacao, então o nome do arquivo que contém a classe deverá ser: class.ajax.validacao.inc.php
Quando a classe for instanciada, seu construtor será chamado sem parâmetros.
No template do processo que utilizará Ajax,é necessário incluir a bibliotecaJavaScript que permite sua utilização.Para isto, foi desenvolvido um plugin do Smarty que insere os arquivos necessários( garantindo que não sejam incluídos maisdeumavez).Para fazer inserção dos arquivosJavaScript, consulte: Inicialização do Ajax
A especificação de qual classe e método será chamado, é feita através do método addVirtualRequest da classe NanoController (JavaScript). Este método aceita três parâmetros, são eles (em ordem):
um identificador da chamada Ajax.
um objeto JavaScript com os atributos:
action: contendo o nome da classe que será chamada
mode: o nome do método da classe que será chamado
um ojeto JavaScript contendo eventuais parâmetros para a chamada (este parâmetro é opcional)
No caso dos dois últimos parâmetros, é possível fazer a definição dos objetos in loco.
Exemplo de utilização
Classe PHP no arquivo class.ajax.minhas_strings.inc.php
class minhas_strings() { var $nome; function minhas_strings() { $this->nome = "Mundo"; } function saudacao() { return "Olá " . $this->nome; } function adeus() { return "Adeus " . $this->nome . " cruel"; } function saudacaoEspecial($params) { return $params['cumprimento'] . " " . $this->nome; } }
No meu arquivo de templates, eu posso fazer uma chamada para um método da classe Ajax da seguinte forma:
{wf_ajax_init} <script language="javascript"> {literal} var nc = new NanoController(); nc.setWfUrl(); nc.setSuccessHandler(exibirResultado); nc.addVirtualRequest('chamada_1', { action : 'minhas_strings', mode : 'saudacao' }); nc.sendRequest(); function exibirResultado(dados) { /* irá exibir um alerta com a string "Olá Mundo" */ alert(dados['chamada_1']['data']); } {/literal} </script>
A tag Smarty {{literal}} indica que o smarty não irá tentar interpetrar as chaves como sendo seus delimitadores.
Com a biblioteca de Ajax utilizada, que é a NanoAjax, é possível fazer várias chamadas a métodos em uma única chamada Ajax. Sendo assim, o exemplo abaixo chama dois métodos da classe "minhas_strings" (as chamadas não precisam ficar restritas a apenas uma classe).
{wf_ajax_init} <script language="javascript"> {literal} var nc = new NanoController(); nc.setWfUrl(); nc.setSuccessHandler(exibirResultado); nc.addVirtualRequest('chamada_1', { action : 'minhas_strings', mode : 'saudacao' }); nc.addVirtualRequest('chamada_2', { action : 'minhas_strings', mode : 'adeus' }); nc.sendRequest(); function exibirResultado(dados) { /* irá exibir um alerta com a string "Olá Mundo" */ alert(dados['chamada_1']['data']); /* irá exibir um alerta com a string "Adeus Mundo cruel" */ alert(dados['chamada_2']['data']); } {/literal} </script>
Também é possível chamar um método passando um ou mais parâmetros. Um exemplo que passa um parâmetro pode ser visto abaixo:
{wf_ajax_init} <script language="javascript"> {literal} var nc = new NanoController(); nc.setWfUrl(); nc.setSuccessHandler(exibirResultado); nc.addVirtualRequest('chamada_especial', { action : 'minhas_strings', mode : 'saudacaoEspecial' }, { cumprimento: 'Oi' }); nc.sendRequest(); function exibirResultado(dados) { /* irá exibir um alerta com a string "Oi Mundo" */ alert(dados['chamada_especial']['data']); } {/literal} </script>
Uma outra coisa que pode ser feita, é a utilização de um handler próprio no caso de um erro na chamada Ajax. Exemplo:
{wf_ajax_init} <script language="javascript"> {literal} var nc = new NanoController(); nc.setWfUrl(); nc.setSuccessHandler(exibirResultado); nc.setExceptionHandler(tratarErro); nc.addVirtualRequest('vai_dar_erro', { action : 'minhas_strings', mode : 'metodo_inexistente' }, { cumprimento: 'Oi' }); nc.sendRequest(); /* esta função não será chamada pois ocorrerá um erro devido à não existência do método [[BR]] "metodo_inexistente" da classe "minhas_strings" */ function exibirResultado(dados) { alert(dados['vai_dar_erro']['data']); } /* função que trata erros na chamada Ajax */ function tratarErro(dados) { /* aqui o meu erro será tratado */ alert("Houve um erro neste procedimento.\nPor favor, contacte o administrador"); } {/literal} </script>
Agora um outro exemplo, que faz acesso a banco de dados:
{wf_ajax_init} <script language="javascript"> {literal} function alterarValor(externoID, p_usuario, p_data, p_valor) { var resultAlterarValor = function(data) { if (data['valor']['data']['erro']) { alert(data['valor']['data']['erro']); /* trata o erro */ } else { var externo = $(externoID); externo.innerHTML = data['valor']['data']['valor']; } } var nc = new NanoController(); nc.setWfUrl(); nc.setSuccessHandler(resultAlterarValor); nc.addVirtualRequest('valor', { action : 'definir_valores', mode : 'valor' }, { usuario: p_usuario, data_hora: p_data, valor: p_valor }); nc.sendRequest(); $(externoID).innerHTML = '<font color="grey">atualizando..</font>'; }
A classe php invocada por este código JavaScript seria: (class.ajax.definir_valores.php)
<?php class definir_valores { var $db; var $schema; function definir_valores() { $this->db = wf_create_object('wf_db'); $this->db->connect(); $this->schema = "ligacoes_telefonicas"; } function valor($params) { $output = array(); $valor = str_replace(",", ".", str_replace(".", "", $params['valor'])); $usuario = (int) $params['usuario']; $data_hora = $params['data_hora']; if (!is_numeric($valor)) $output['erro'] = "\"" . $params['valor'] . "\" é um valor inválido"; else { $valor = (double) $valor; $sqlStatement = "UPDATE {$this->schema}.ligacao_telefonica SET valor = ? WHERE (data_hora = ?) AND (usuario = ?)"; $resultado = $this->db->query($sqlStatement, array($valor, $data_hora, $usuario)); $this->db->disconnect(); if ($resultado) $output['valor'] = $params['valor']; else $output['erro'] = "Ocorreu um erro ao atualizar o valor desta ligação."; } return $output; } } ?>
Tratando Sessões Expiradas
A partir do Workflow 1.7.00.000, foram feitas algumas modificações (inclusive no NanoAjax) que permitem o tratamento de sessões expiradas. Quando uma sessão expira e é feita uma chamada Ajax, a resposta incluirá uma exceção com valor __NANOAJAX_SESSION_EXPIRED__ para cada chamada virtual. O tratamento é bem simples, como pode ser visto abaixo (JavaScript):
/* função que trata as exceções */ function tratamentoExcecao(header, body, exceptionResponse) { var ajaxResult = JSON.parse(body); for (var requestIdentifier in ajaxResult) { /* verifica se a exceção é originária da sessão expirada */ if (ajaxResult[requestIdentifier]['exception'] == '__NANOAJAX_SESSION_EXPIRED__') { /* informa o usuário sobre a sessão expirada e o envia para a tela de login */ alert('Sua sessão expirou'); window.location = 'login.php'; return ; } } } var nc = new NanoController(); nc.setExceptionHandler(tratamentoExcecao); // define que nossa função será usada para tratamento de exceções /* ... */ nc.sendRequest();