Parte 1 – Parte 2 – Parte 3 – Parte 4.1 – Parte 4.2
Finalmente, chegamos na última parte do nosso tutorial. Desde a primeira parte fomos construindo incrementalmente a arquitetura do nosso projeto e fomos testando como comunicar a camada do cliente (feita no Flash Builder e compilada pelo Flex) com a camada do servidor, onde usamos Java e o BlazeDS. Até o momento, nosso projeto no NetBeans tem a seguinte estrutura:
Com essa estrutura, temos então toda a camada de persistência e a camada de serviços pronta para ser utilizada. Percebam que os serviços dos exemplos anteriores ainda continuam no projeto. Eles não serão retirados, pois vamos deixar todos os exemplos dentro de um único projeto. Vamos agora trabalhar na nossa interface gráfica e em como integrá-la ao nosso backend.
Quando enviamos valores (primitivos e/ou objetos) através do canal do AMF, implementado pelo BlazeDS, algumas conversões são feitas. O BlazeDS acaba funcionando como um tradutor e um interpretador. Ele serializa os objetos em Java para o formato do AMF e envia para o cliente, que por sua vez entende o AMF e deserializa os dados no formato de objetos ActionScript. O contrário também é verdade. O cliente serializa objetos de ActionScript para AMF e envia para a camada em Java, onde o BlazeDS deserializa os dados em AMF convertendo-os em objetos Java. Para os tipos padrão do Java (int, double, String, etc.) e do ActionScript (int, uint, Number, String, etc.) existe um padrão de conversão. A seguir, nas próximas duas tabelas estão listadas a maioria dessas conversões.
Deserialização de objetos ActionScript para objetos Java
ActionScript | Java |
---|---|
Array (denso) | java.util.List |
Array (esparso) | java.util.Map |
Boolean, ou as Strings “true” ou “false” | java.lang.Boolean |
Date | java.util.Date |
int/uint | java.lang.Integer |
Number | java.lang.Double |
String | java.lang.String |
undefined | null |
Deserialização de objetos Java para objetos ActionScript
Java | ActionScript |
---|---|
java.util.Collection e subclasses, Object[] (array nativo) | Array (denso) |
java.util.Map | Array (esparso) |
Boolean, boolean | Boolean |
java.util.Date, java.util.Calendar, java.sql.Timestamp, java.sql.Time, java.sql.Date |
Date |
java.lang.Double, java.lang.Long, java.lang.Float, java.lang.Integer, java.lang.Short, java.lang.Byte, java.math.BigDecimal, java.math.BigInteger, String, tipos primitivos: double, long, float, int, short, byte. |
int/uint (truncagem automática) |
java.lang.Double, java.lang.Long, java.lang.Float, java.lang.Integer, java.lang.Short, java.lang.Byte, java.math.BigDecimal, java.math.BigInteger, String, 0 (zero) se for enviado null, tipos primitivos: double, long, float, int, short, byte |
Number |
java.lang.String, char[], enum | String |
null | undefined |
A primeira tabela é relativamente simples, pois um tipo ActionScript vira um tipo em Java. A única observação a ser feita é entre a diferenciação de Arrays densos e esparsos. No ActionScript, a classe Array funciona tanto como um array simples como um mapa (dicionário ou array associativo, escolha o nome que gostar mais). Quando um array denso é serializado, ele vira uma lista. Um array denso é aquele onde os valores atribuidos a ele seguem um índice determinado (0, 1, 2, …, n). No array esparso a distribuição é baseada em uma “chave” que é utilizada como índice. Sendo assim, o posicionamento dos dados dentro da estrutura de dados é indeterminada. Note a semelhança com um mapa do Java.
Em relação à segunda tabela, perceba que um ou mais tipos do Java são convertidos em um ou mais tipos em ActionScript. O funcionamento é o seguinte. Imagine que você tem uma variável do tipo Number do lado do ActionScript e então é enviada uma String em Java, que vai representar um número. Quando chegar a String e ela for ser atribuida a um Number, a conversão vai ser automática. Outro exemplo. Imagine que foi enviado um double em Java e que a variável no ActionScript é do tipo uint. Novamente, a conversão vai ser automática, truncando a parte fracionária do double. Note que a conversão é sempre automática quando usamos o AMF.
Essas tabelas são um resumo das tabelas encontradas nos dois links a seguir, que fazem parte da documentação do BlazeDS.
Legal, o problema para tipos padrão está resolvido, mas ai você pergunta:
“Certo, e o que eu faço se eu mandar um Estado, uma Cidade, um Cliente ou mesmo qualquer outro tipo de objeto criado por mim? Como será feita a conversão?”
Vamos às repostas: Por padrão o objeto será convertido em Object no ActionScript, mas isso nos atrapalha, pois fica difícil trabalhar com algo que você (desenvolvedor) não sabe o que é. Como o ActionScript é uma linguagem dinâmica, ele vai resolver as propriedades do objeto para nós automaticamente, mas como já disse, seria legal a gente saber com o que estamos trabalhando, facilitando a nossa vida, tornando o código mais claro. Para isso, precisamos então implementar as classes que enviamos do lado Java para suas correspondentes em ActionScript. Os tipos padrão continuam a mesma coisa (siga as tabelas acima). Vamos então ao primeiro exemplo.
No Flash Builder, no projeto da nossa interface, clique com o botão direito na pasta “src” e escolha New > Package. Dê o nome de “entidades” ao pacote e clique em OK. Lembre-se, para cada classe Java que tivermos (as entidades), teremos uma classe em ActionScript. Sendo assim, teremos três classes em ActionScript: Estado, Cidade e Cliente. Vamos criar então uma classe dentro do pacote criado. Começaremos pela classe Estado. Vamos lá então: botão direito no pacote “entidades”, New > ActionScript Class. Preencha o campo “Name” com “Estado” (sem aspas) e clique no OK. A classe será criada e será aberta no editor. Note que por padrão o Flash Builder vai usar as chaves de abertura de bloco abaixo da declaração dos métodos, etc. Eu não gosto desse padrão, prefiro seguir o padrão do Java, mas você fica livre para escolher o que achar melhor.
Note que a estrutura é parecida com a estrutura de uma classe em Java, menos pelo detalhe do pacote, que também é um bloco em classes ActionScript. Vamos começar a preencher nossa classe. Vamos começar pelos atributos. Note que todos os atributos privados iniciam com um “_”. Isso não é obrigatório, mas já eu explico o motivo.
Classe em Java | Classe em ActionScript |
---|---|
package entidades; public class Estado { private Long id; private String nome; private String sigla; // getters e setters } |
package entidades { public class Estado { private var _id: Number; private var _nome: String; private var _sigla: String; // getters e setters } } |
Perceba então que o Long do Java virou Number no ActionScript e o tipo String no Java virou String no ActionScript. Fácil não é? É só seguir as tabelas do começo do post. Agora quanto ao “_”. No ActionScript, como em outras linguagens, existe um recurso que se chama propriedades. Você cria métodos especiais que são chamados para atualizar os membros privados da classe como se você estivesse acessando esse membro como um membro público. No Java esse recurso não existe, então seguimos o padrão JavaBeans (get e set para expor os atributos de uma classe, etc.). Então para cada par get e set do Java, teremos um par de propriedades na classe ActionScript. Veja então como ficaria o método setId( Long id ) e getId() da classe Java na classe ActionScript.
Gets e Sets (Padrão JavaBeans) | Propriedades em ActionScript |
---|---|
public Long getId() { return id; } public void setId( Long id ) { this.id = id; } |
public function get id(): Number { return _id; } public function set id( valor: Number ): void { _id = valor; } |
Então, enrolei mas ainda não falei do “_”. Note que se caso a nossa variável privada “_id” não tivesse o “_”, o nome dela entraria em conflito com o nome da pripriedade “id” que foi criada para acessar a variável privada. Sendo assim, normalmente, em linguagens que suportam propriedades, usa-se o “_” como prefixo para as variáveis privadas. Entendido isso e as propriedades, vamos então ao código completo da nossa classe Estado em ActionScript:
entidades.Estado.as
package entidades { [Bindable] [RemoteClass(alias="entidades.Estado")] public class Estado { private var _id: Number; private var _nome: String; private var _sigla: String; public function get id(): Number { return _id; } public function set id( valor: Number ): void { _id = valor; } public function get nome(): String { return _nome; } public function set nome( valor: String ): void { _nome = valor; } public function get sigla(): String { return _sigla; } public function set sigla( valor: String ): void { _sigla = valor; } public function toString(): String { return _nome; } } }
Note ainda que antes da declaração da classe Estado, temos ainda duas Metadata Tags (parecidas com as Annotations em Java). Segue a explicação de cada uma:
- [Bindable]: Essa tag informa que os objetos do tipo Estado podem ser amarrados aos componentes gráficos e caso o estado do objeto seja alterado, ou seja, alguma de suas propriedades tenha o valor alterado, essas mudanças serão refletidas automaticamente no componente gráfico que o estiver usando.
- [RemoteClass(alias=”entidades.Estado”)]: Essa tag é muito importante para nós. Ela diz que essa classe é uma classe remota, ou seja, que os objetos dela podem ser enviados via AMF. Note que a propriedade “alias” tem como valor o nome completo da classe em Java. Caso nossas entidades Java estivesse no pacote “model” por exemplo, o “alias” teria que ser “model.Estado”. Como estamos usando uma estrutura de pacotes igual nos dois projetos, o “alias” é igual ao do projeto em Flex, mas não se confunda. O “alias” é sempre igual ao nome completo da classe em Java.
Perceba também que implementamos o método toString() da classe Estado. O toString() no Flex tem o mesmo propósito que no Java, ou seja, fornecer uma representação em String do estado de um determinado objeto. No nosso caso, estamos apenas retornando o nome do Estado (e faremos isso para as outra duas classes). A vantagem disso é que os objetos serão mostrados nos componentes que os usarem (combos por exemplo) utilizando o retorno do toString().
Pronto, você já sabe todos os detalhes. Seria legal agora, como exercício, você criar as duas classes que estão faltando, ou seja, as classes Cidade e Cliente. Assim que criar, compare com as que estou listando abaixo. Como fiz nas entidades em Java, não vou colocar os gets e os sets para poupar espaço.
entidades.Cidade.as
package entidades { [Bindable] [RemoteClass(alias="entidades.Cidade")] public class Cidade { private var _id: Number; private var _nome: String; private var _estado: Estado; // getters e setters public function toString(): String { return _nome; } } }
entidades.Cliente.as
package entidades { [Bindable] [RemoteClass(alias="entidades.Cliente")] public class Cliente { private var _id: Number; private var _nome: String; private var _sobrenome: String; private var _cpf: String; private var _dataNascimento: Date; private var _rua: String; private var _numero: String; private var _cep: String; private var _cidade: Cidade; // getters e setters public function toString(): String { return _nome; } } }
Ótimo, temos todas as nossas classes mapeadas, agora vamos partir para a interface gráfica. Vamos criar outro arquivo mxml para criarmos nossa interface. Para isso, no “(default package)”, clique com o botão direito e escolhe New > MXML Application. Dê o nome de “CRUD” (sem aspas) e escolha “None” no layout. Clique em Finish. O novo arquivo será criado e aberto no editor.
Caso você execute diretamente o projeto dentro do Flash Builder, para ver como está ficando no navegador, você vai perceber que a interface anterior ainda vai ser exibida. Para mudar isso, precisamos fazer com que o CRUD.mxml seja a aplicação padrão. Para isso, clique com o botão direito no arquivo CRUD.mxml e escolha a opção “Set as Default Application”. Você vai perceber que vai aparecer uma bolinha azul no ícone do arquivo (que antes estava no IntergracaoFlexJavaGUI.mxml).
Ainda falta configurar o index.jsp lá no NetBeans para que seja redirecionado para o CRUD.html ao invés do IntegracaoFlexJavaGUI.html. Como exercício, faça a alteração então na tag <meta> lá do seu index.jsp e rode o projeto. Uma tela em branco deve ser carregada.
O próximo passo agora é criar os <s:RemoteObject> que vão fazer a ponte para a camada em Java e montar a interface gráfica. Depois iremos implementar os eventos para dar vida à interface. Segue então o CRUD.mxml com a interface pronta, os servicos mapeados, todos os manipuladores de eventos criados e registrados (aplicação, botões e tabelas) e o id de todos os componentes criados, além do prinscreen da interface.
CRUD.mxml
<?xml version="1.0" encoding="utf-8"?> <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600" creationComplete="creationCompleteHandler(event)"> <fx:Script> <![CDATA[ import entidades.Cidade; import entidades.Cliente; import entidades.Estado; import mx.collections.ArrayCollection; import mx.controls.Alert; import mx.events.FlexEvent; import mx.events.ItemClickEvent; import mx.events.ListEvent; import mx.rpc.events.FaultEvent; import mx.rpc.events.ResultEvent; // armazenam a referência para as instâncias selecionadas no momento. private var estadoSelec: Estado; private var cidadeSelec: Cidade; private var clienteSelec: Cliente; private function faultHandler( event: FaultEvent ): void { Alert.show( event.fault.toString(), "ERRO" ); } // handlers para estado private function estadoSaveResultHandler( event: ResultEvent ): void { } private function estadoUpdateResultHandler( event: ResultEvent ): void { } private function estadoDeleteResultHandler( event: ResultEvent ): void { } private function estadoListAllResultHandler( event: ResultEvent ): void { tabelaEstados.dataProvider = event.result; comboEstadoCidade.dataProvider = event.result as ArrayCollection; } private function btnNovoEstadoClickHandler( event: MouseEvent ): void { } private function btnSalvarEstadoClickHandler( event: MouseEvent ): void { } private function btnExcluirEstadoClickHandler( event: MouseEvent ): void { } private function tabelaEstadosClickHandler( event: ListEvent ): void { } // handlers para cidade private function cidadeSaveResultHandler( event: ResultEvent ): void { } private function cidadeUpdateResultHandler( event: ResultEvent ): void { } private function cidadeDeleteResultHandler( event: ResultEvent ): void { } private function cidadeListAllResultHandler( event: ResultEvent ): void { tabelaCidades.dataProvider = event.result; comboCidadeCliente.dataProvider = event.result as ArrayCollection; } private function btnNovoCidadeClickHandler( event: MouseEvent ): void { } private function btnSalvarCidadeClickHandler( event: MouseEvent ): void { } private function btnExcluirCidadeClickHandler( event: MouseEvent ): void { } private function tabelaCidadesClickHandler( event: ListEvent ): void { } // handlers para cliente private function clienteSaveResultHandler( event: ResultEvent ): void { } private function clienteUpdateResultHandler( event: ResultEvent ): void { } private function clienteDeleteResultHandler( event: ResultEvent ): void { } private function clienteListAllResultHandler( event: ResultEvent ): void { tabelaClientes.dataProvider = event.result; } private function btnNovoClienteClickHandler( event: MouseEvent ): void { } private function btnSalvarClienteClickHandler( event: MouseEvent ): void { } private function btnExcluirClienteClickHandler( event: MouseEvent ): void { } private function tabelaClientesClickHandler( event: MouseEvent ): void { } // handlers da aplicação private function creationCompleteHandler( event: FlexEvent ): void { servEstado.listAll(); servCidade.listAll(); servCliente.listAll(); } ]]> </fx:Script> <fx:Declarations> <s:RemoteObject id="servEstado" destination="estadoServices" showBusyCursor="true"> <s:method name="save" fault="faultHandler(event)" result="estadoSaveResultHandler(event)"/> <s:method name="update" fault="faultHandler(event)" result="estadoUpdateResultHandler(event)"/> <s:method name="delete" fault="faultHandler(event)" result="estadoDeleteResultHandler(event)"/> <s:method name="listAll" fault="faultHandler(event)" result="estadoListAllResultHandler(event)"/> </s:RemoteObject> <s:RemoteObject id="servCidade" destination="cidadeServices" showBusyCursor="true"> <s:method name="save" fault="faultHandler(event)" result="cidadeSaveResultHandler(event)"/> <s:method name="update" fault="faultHandler(event)" result="cidadeUpdateResultHandler(event)"/> <s:method name="delete" fault="faultHandler(event)" result="cidadeDeleteResultHandler(event)"/> <s:method name="listAll" fault="faultHandler(event)" result="cidadeListAllResultHandler(event)"/> </s:RemoteObject> <s:RemoteObject id="servCliente" destination="clienteServices" showBusyCursor="true"> <s:method name="save" fault="faultHandler(event)" result="clienteSaveResultHandler(event)"/> <s:method name="update" fault="faultHandler(event)" result="clienteUpdateResultHandler(event)"/> <s:method name="delete" fault="faultHandler(event)" result="clienteDeleteResultHandler(event)"/> <s:method name="listAll" fault="faultHandler(event)" result="clienteListAllResultHandler(event)"/> </s:RemoteObject> </fx:Declarations> <s:Panel x="10" y="10" width="567" height="565" title="Cadastro de Clientes"> <mx:DataGrid y="6" left="10" right="10" height="196" id="tabelaClientes" click="tabelaClientesClickHandler(event)"> <mx:columns> <mx:DataGridColumn headerText="Nome" dataField="nome"/> <mx:DataGridColumn headerText="Sobrenome" dataField="sobrenome"/> <mx:DataGridColumn headerText="CPF" dataField="cpf" width="100"/> <mx:DataGridColumn dataField="dataNascimento" headerText="Dt. Nasc" width="80"/> <mx:DataGridColumn dataField="cidade.nome" headerText="Cidade"/> </mx:columns> </mx:DataGrid> <mx:Form y="210" left="10" right="10" height="281"> <mx:FormItem label="ID:"> <s:TextInput width="40" enabled="false" id="fieldIdCliente"/> </mx:FormItem> <mx:FormItem label="Nome:"> <s:TextInput id="fieldNomeCliente"/> </mx:FormItem> <mx:FormItem label="Sobrenome:"> <s:TextInput id="fieldSobrenomeCliente"/> </mx:FormItem> <mx:FormItem label="CPF:"> <s:TextInput width="95" id="fieldCPFCliente"/> </mx:FormItem> <mx:FormItem label="Data de Nascimento:"> <mx:DateField id="fieldDataNascCliente"/> </mx:FormItem> <mx:FormItem label="Rua:"> <s:TextInput id="fieldRuaCliente" width="249"/> </mx:FormItem> <mx:FormItem label="Número:"> <s:TextInput width="59" id="fieldNumeroCliente"/> </mx:FormItem> <mx:FormItem label="CEP:"> <s:TextInput width="94" id="fieldCEPCliente"/> </mx:FormItem> <mx:FormItem label="Cidade:"> <s:ComboBox width="200" id="comboCidadeCliente"/> </mx:FormItem> </mx:Form> <s:Button x="329" y="499" label="Novo" click="btnNovoClienteClickHandler(event)"/> <s:Button x="407" y="499" label="Salvar" click="btnSalvarClienteClickHandler(event)"/> <s:Button y="499" label="Excluir" right="10" click="btnExcluirClienteClickHandler(event)"/> </s:Panel> <s:Panel x="585" y="10" width="360" height="277" title="Cadastro de Cidades"> <mx:DataGrid x="10" y="10" id="tabelaCidades" width="338" height="106" itemClick="tabelaCidadesClickHandler(event)"> <mx:columns> <mx:DataGridColumn headerText="Nome" dataField="nome" width="180"/> <mx:DataGridColumn headerText="Estado" dataField="estado.nome" width="150"/> </mx:columns> </mx:DataGrid> <mx:Form y="120" left="10" right="10" height="83"> <mx:FormItem label="Nome:"> <s:TextInput id="fieldNomeCidade" width="189"/> </mx:FormItem> <mx:FormItem label="Estado:"> <s:ComboBox id="comboEstadoCidade"/> </mx:FormItem> </mx:Form> <s:Button x="126" y="211" label="Novo" click="btnNovoCidadeClickHandler(event)"/> <s:Button x="204" y="211" label="Salvar" click="btnSalvarCidadeClickHandler(event)"/> <s:Button x="278" y="211" label="Excluir" click="btnExcluirCidadeClickHandler(event)"/> </s:Panel> <s:Panel x="585" y="295" width="360" height="280" title="Cadastro de Estados"> <mx:DataGrid y="10" id="tabelaEstados" left="10" right="10" height="107" itemClick="tabelaEstadosClickHandler(event)"> <mx:columns> <mx:DataGridColumn headerText="Nome" dataField="nome" width="150" resizable="true"/> <mx:DataGridColumn headerText="Sigla" dataField="sigla" width="80" resizable="true"/> </mx:columns> </mx:DataGrid> <mx:Form y="121" right="11" left="9" height="86"> <mx:FormItem label="Nome:"> <s:TextInput width="187" id="fieldNomeEstado"/> </mx:FormItem> <mx:FormItem label="Sigla:"> <s:TextInput width="40" id="fieldSiglaEstado"/> </mx:FormItem> </mx:Form> <s:Button x="121" y="215" label="Novo" click="btnNovoEstadoClickHandler(event)"/> <s:Button x="199" y="215" label="Salvar" click="btnSalvarEstadoClickHandler(event)"/> <s:Button x="277" y="215" label="Excluir" click="btnExcluirEstadoClickHandler(event)"/> </s:Panel> </s:Application>
Pronto, temos toda a estrutura da aplicação, agora só falta implementar os métodos que tratam os eventos. Primeiramente vou explicar o que já foi feito. Percebeam que no ínicio do CRUD.mxml, na tag <s:Application>, foi registrado um ouvinte para o evento creationComplete. Este evento é disparado quando toda a estrutura da aplicação está criada e pronta para funcionar. No corpo do método que trata o evento (linha 132) você pode perceber que são executados os métodos listAll dos três <s:RemoteObject>. Isso faz com que todas as instâncias de todas as entidades sejam carregadas. Lembre-se que o resultado da execução do serviço é tratado nos métodos registrados para o evento result.
Na linha 43, o método estadoListAllResultHandler obtém o resultado da execução (a lista de estados retornada pelo serviço) e a associa diretamente ao dataProvider da tabelaEstados. Isso faz com que a tabela mostre os Estados obtidos. Além de ligar os dados à tabela, o resultado também é associado ao combo de estados (dentro do formulário de cidades), sendo que para fazer isso existe a necessidade de fazer o cast do resultado para um ArrayCollection (o que não precisa ser feito para a tabela). Note que os outros métodos result (para Cidade e Cliente) executam tarefas parecidas.
Para que uma tabela (<d:DataGrid>) exiba corretamente os dados da lista de objetos que é atribuida ao seu dataProvider, você precisa configurar a propriedade dataField de cada coluna. Por exemplo, na tabela de clientes, a primeira coluna tem o dataField configurado como “name”, pois a lista de objetos que será usada como dataProvider, contém Clientes, que tem nome como propriedade. Ou seja, o dataField tem que ter o mesmo nome da propriedade dos objetos que são passados ao dataProvider. Veja o dataField da coluna na linha 239. O valor é “cidade.nome”, pois uma instância de Cliente tem uma propriedade que chama “cidade”, sendo que essa propriedade é uma instância de Cidade, que por sua vez, tem um “nome”.
Acredito que a maioria dos detalhes seja fácil de ser entendida se você der uma estudada no código. Agora vamos passar para a implementação de todos os métodos para o cadastro de Estados. Irei explicar um por um, com comentários dentro do código de cada método que vai ser apresentado. Lembre-se que caso você esteja acompanhando o tutorial e estiver implementando passo a passo, os métodos já estão declarados, bastando inserir o código dentro de cada um.
Métodos para o cadastro de Estados
// handlers para estado private function estadoSaveResultHandler( event: ResultEvent ): void { // se entrou no método, quer dizer que o estado foi salvo, sendo assim, // atualiza a tabela chamando o listAll e reinicia o formulário. servEstado.listAll(); resetFormEstados(); } private function estadoUpdateResultHandler( event: ResultEvent ): void { // se entrou no método, quer dizer que o estado foi alterado, sendo assim, // atualiza a tabela chamando o listAll e reinicia o formulário. servEstado.listAll(); resetFormEstados(); } private function estadoDeleteResultHandler( event: ResultEvent ): void { // se entrou no método, quer dizer que o estado foi excluído, sendo assim, // atualiza a tabela chamando o listAll e reinicia o formulário. servEstado.listAll(); resetFormEstados(); } private function estadoListAllResultHandler( event: ResultEvent ): void { // configura o dataProvider da tabela com os dados que vem no result. // nesse caso, é uma lista de Estados. tabelaEstados.dataProvider = event.result; // o dataProvider de um combo espera por algo que implemente a interface IList // o result do serviço retorna uma lista, então podemos fazer o cast para ArrayCollection comboEstadoCidade.dataProvider = event.result as ArrayCollection; } private function btnNovoEstadoClickHandler( event: MouseEvent ): void { // reseta o formulário resetFormEstados(); } private function btnSalvarEstadoClickHandler( event: MouseEvent ): void { // estadoSelec é diferente de null ou de undefined? if ( estadoSelec ) { // então existe um estado selecionado // altera os valores estadoSelec.nome = fieldNomeEstado.text; estadoSelec.sigla = fieldSiglaEstado.text; // manda atualizar servEstado.update(estadoSelec); } else { // não tem estado selecionado, cria um novo var o: Estado = new Estado(); // insere os valores o.nome = fieldNomeEstado.text; o.sigla = fieldSiglaEstado.text; // manda salvar servEstado.save(o); } } private function btnExcluirEstadoClickHandler( event: MouseEvent ): void { // estadoSelec é diferente de null ou de undefined? if ( estadoSelec ) { // sim, então exclui o estado. // seria legal uma confirmação aqui não acha? fica de exercício para você hehehe // obs: note que não chamamos servEstado.delete diretamente, pois delete // é uma pavra-chave no ActionScript. Sendo assim, usamos a seguinte // instrução para invoca o método sem ter problemas: servEstado.getOperation( "delete" ).send( estadoSelec ); } } private function tabelaEstadosClickHandler( event: ListEvent ): void { // estadoSelec recebe o item selecionado na lista estadoSelec = tabelaEstados.selectedItem as Estado; // preenche os campos fieldNomeEstado.text = estadoSelec.nome; fieldSiglaEstado.text = estadoSelec.sigla; } // função para limpar o campo e resetar a referência para o estado selecionado // esse método é novo, não foi apresentado no código anterior. private function resetFormEstados(): void { // limpa os campos fieldNomeEstado.text = ""; fieldSiglaEstado.text = ""; // não tem mais estados selecionados estadoSelec = null; }
Teste o exemplo. Veja que agora todo o cadastro do Estados funciona, e caso um Estado seja alterado ou um novo Estado seja inserido, tanto a tabela de Estados quanto o combo de Estados do cadastro de cidades são atualizados. Note que enviamos os objetos inteiros como parâmetros nos métodos save, update e delete do serviço de estados. Percebeu como é fácil? A serialização/deserialização é transparatente para nós.
Agora, antes de finalizarmos, ainda precisamos resolver um último detalhe: Arrumar o formato da data de nascimento na tabela do cliente. Para isso, vamos criar um renderizador para a coluna da data. O primeiro passo é escolher a tabela de clientes. Clique na tabela, e na aba de propriedades, clique no botão “Configure Columns…”.
Ao clicar, a janela “Configura Columns” vai aparecer. Do lado esquerdo estão listadas as colunas da tabela. Selecione a “Dt. Nasc” e nas propriedades da coluna, clique no botão rosa (destacado em laranja na Figura abaixo) e escolha “Create Item Renderer…”.
Uma janela para criar o novo renderizador vai aparecer. Deixe o “Package” vazio e preencha o “Name” com “DateRenderer” (sem aspas). Clique em “Finish” para criar o renderizador e veja que agora o nome do renderizador aparece nas propriedades da coluna. Dê OK na janela de configuração das colunas. Perceba que um arquivo chamado DateRenderer.mxml foi criado. Vamos editá-lo. Basicamente iremos usar um <mx:DateFormatter> para formatar o label que vai conter os dados. Segue então o código do nosso renderizador, basta colar dentro do arquivo.
DateRenderer.mxml
<?xml version="1.0" encoding="utf-8"?> <s:MXDataGridItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" focusEnabled="true"> <fx:Declarations> <mx:DateFormatter id="formatadorData" formatString="DD/MM/YYYY"/> </fx:Declarations> <s:Label id="lblData" width="100%" height="100%" verticalAlign="middle" paddingLeft="10" text="{formatadorData.format(dataGridListData.label)}"/> </s:MXDataGridItemRenderer>
Salve o projeto e teste novamente. Você vai ver que agora a data de nascimento dos clientes vai ser formatada corretamente. Com isso terminamos as explicações referentes à integração do Flex com o Java, além de termos feito um exemplo com muitos recursos. Sugiro agora que como exercício você tente implementar os métodos que estão faltando. Caso tenha dúvidas, verifique o código completo (e final) abaixo.
CRUD.mxml (final)
<?xml version="1.0" encoding="utf-8"?> <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600" creationComplete="creationCompleteHandler(event)"> <fx:Script> <![CDATA[ import entidades.Cidade; import entidades.Cliente; import entidades.Estado; import mx.collections.ArrayCollection; import mx.controls.Alert; import mx.events.FlexEvent; import mx.events.ItemClickEvent; import mx.events.ListEvent; import mx.rpc.events.FaultEvent; import mx.rpc.events.ResultEvent; // armazenam a referência para as instâncias selecionadas no momento. private var estadoSelec: Estado; private var cidadeSelec: Cidade; private var clienteSelec: Cliente; private function faultHandler( event: FaultEvent ): void { Alert.show( event.fault.toString(), "ERRO" ); } // handlers para estado private function estadoSaveResultHandler( event: ResultEvent ): void { // se entrou no método, quer dizer que o estado foi salvo, sendo assim, // atualiza a tabela chamando o listAll e reinicia o formulário. servEstado.listAll(); resetFormEstados(); } private function estadoUpdateResultHandler( event: ResultEvent ): void { // se entrou no método, quer dizer que o estado foi alterado, sendo assim, // atualiza a tabela chamando o listAll e reinicia o formulário. servEstado.listAll(); resetFormEstados(); } private function estadoDeleteResultHandler( event: ResultEvent ): void { // se entrou no método, quer dizer que o estado foi excluído, sendo assim, // atualiza a tabela chamando o listAll e reinicia o formulário. servEstado.listAll(); resetFormEstados(); } private function estadoListAllResultHandler( event: ResultEvent ): void { // configura o dataProvider da tabela com os dados que vem no result. // nesse caso, é uma lista de Estados. tabelaEstados.dataProvider = event.result; // o dataProvider de um combo espera por algo que implemente a interface IList // o result do serviço retorna uma lista, então podemos fazer o cast para ArrayCollection comboEstadoCidade.dataProvider = event.result as ArrayCollection; } private function btnNovoEstadoClickHandler( event: MouseEvent ): void { // reseta o formulário resetFormEstados(); } private function btnSalvarEstadoClickHandler( event: MouseEvent ): void { // estadoSelec é diferente de null ou de undefined? if ( estadoSelec ) { // então existe um estado selecionado // altera os valores estadoSelec.nome = fieldNomeEstado.text; estadoSelec.sigla = fieldSiglaEstado.text; // manda atualizar servEstado.update(estadoSelec); } else { // não tem estado selecionado, cria um novo var o: Estado = new Estado(); // insere os valores o.nome = fieldNomeEstado.text; o.sigla = fieldSiglaEstado.text; // manda salvar servEstado.save(o); } } private function btnExcluirEstadoClickHandler( event: MouseEvent ): void { // estadoSelec é diferente de null ou de undefined? if ( estadoSelec ) { // sim, então exclui o estado. // seria legal uma confirmação aqui não acha? fica de exercício hehehe // obs: note que não chamamos servEstado.delete diretamente, pois delete // é uma pavra-chave no ActionScript. Sendo assim, usamos a seguinte // instrução para invoca o método sem ter problemas: servEstado.getOperation( "delete" ).send( estadoSelec ); } } private function tabelaEstadosClickHandler( event: ListEvent ): void { // estadoSelec recebe o item selecionado na lista estadoSelec = tabelaEstados.selectedItem as Estado; // preenche os campos fieldNomeEstado.text = estadoSelec.nome; fieldSiglaEstado.text = estadoSelec.sigla; } // função para limpar o campo e resetar a referência para o estado selecionado // esse método é novo, não foi apresentado no código anterior. private function resetFormEstados(): void { // limpa os campos fieldNomeEstado.text = ""; fieldSiglaEstado.text = ""; // não tem mais estados selecionados estadoSelec = null; } // handlers para cidade private function cidadeSaveResultHandler( event: ResultEvent ): void { servCidade.listAll(); resetFormCidades(); } private function cidadeUpdateResultHandler( event: ResultEvent ): void { servCidade.listAll(); resetFormCidades(); } private function cidadeDeleteResultHandler( event: ResultEvent ): void { servCidade.listAll(); resetFormCidades(); } private function cidadeListAllResultHandler( event: ResultEvent ): void { tabelaCidades.dataProvider = event.result; comboCidadeCliente.dataProvider = event.result as ArrayCollection; } private function btnNovoCidadeClickHandler( event: MouseEvent ): void { resetFormCidades(); } private function btnSalvarCidadeClickHandler( event: MouseEvent ): void { if ( cidadeSelec ) { cidadeSelec.nome = fieldNomeCidade.text; cidadeSelec.estado = comboEstadoCidade.selectedItem as Estado; servCidade.update(cidadeSelec); } else { var o: Cidade = new Cidade(); o.nome = fieldNomeCidade.text; o.estado = comboEstadoCidade.selectedItem as Estado; servCidade.save(o); } } private function btnExcluirCidadeClickHandler( event: MouseEvent ): void { if ( cidadeSelec ) { servCidade.getOperation( "delete" ).send( cidadeSelec ); } } private function tabelaCidadesClickHandler( event: ListEvent ): void { cidadeSelec = tabelaCidades.selectedItem as Cidade; fieldNomeCidade.text = cidadeSelec.nome; comboEstadoCidade.selectedItem = cidadeSelec.estado; } private function resetFormCidades(): void { fieldNomeCidade.text = ""; comboEstadoCidade.selectedIndex = -1; cidadeSelec = null; } // handlers para cliente private function clienteSaveResultHandler( event: ResultEvent ): void { servCliente.listAll(); resetFormClientes(); } private function clienteUpdateResultHandler( event: ResultEvent ): void { servCliente.listAll(); resetFormClientes(); } private function clienteDeleteResultHandler( event: ResultEvent ): void { servCliente.listAll(); resetFormClientes(); } private function clienteListAllResultHandler( event: ResultEvent ): void { tabelaClientes.dataProvider = event.result; } private function btnNovoClienteClickHandler( event: MouseEvent ): void { resetFormClientes(); } private function btnSalvarClienteClickHandler( event: MouseEvent ): void { if ( clienteSelec ) { clienteSelec.nome = fieldNomeCliente.text; clienteSelec.sobrenome = fieldSobrenomeCliente.text; clienteSelec.cpf = fieldCPFCliente.text; clienteSelec.dataNascimento = fieldDataNascCliente.selectedDate; clienteSelec.rua = fieldRuaCliente.text; clienteSelec.numero = fieldNumeroCliente.text; clienteSelec.cep = fieldCEPCliente.text; clienteSelec.cidade = comboCidadeCliente.selectedItem as Cidade; servCliente.update(clienteSelec); } else { var o: Cliente = new Cliente(); o.nome = fieldNomeCliente.text; o.sobrenome = fieldSobrenomeCliente.text; o.cpf = fieldCPFCliente.text; o.dataNascimento = fieldDataNascCliente.selectedDate; o.rua = fieldRuaCliente.text; o.numero = fieldNumeroCliente.text; o.cep = fieldCEPCliente.text; o.cidade = comboCidadeCliente.selectedItem as Cidade; servCliente.save(o); } } private function btnExcluirClienteClickHandler( event: MouseEvent ): void { if ( clienteSelec ) { servCliente.getOperation( "delete" ).send( clienteSelec ); } } private function tabelaClientesClickHandler( event: MouseEvent ): void { clienteSelec = tabelaClientes.selectedItem as Cliente; fieldIdCliente.text = String( clienteSelec.id ); fieldNomeCliente.text = clienteSelec.nome; fieldSobrenomeCliente.text = clienteSelec.sobrenome; fieldCPFCliente.text = clienteSelec.cpf; fieldDataNascCliente.selectedDate = clienteSelec.dataNascimento; fieldRuaCliente.text = clienteSelec.rua; fieldNumeroCliente.text = clienteSelec.numero; fieldCEPCliente.text = clienteSelec.cep; comboCidadeCliente.selectedItem = clienteSelec.cidade; } private function resetFormClientes(): void { fieldIdCliente.text = ""; fieldNomeCliente.text = ""; fieldSobrenomeCliente.text = ""; fieldCPFCliente.text = ""; fieldDataNascCliente.selectedDate = null; fieldRuaCliente.text = ""; fieldNumeroCliente.text = ""; fieldCEPCliente.text = ""; comboCidadeCliente.selectedIndex = -1; clienteSelec = null; } // handlers da aplicação private function creationCompleteHandler( event: FlexEvent ): void { servEstado.listAll(); servCidade.listAll(); servCliente.listAll(); } ]]> </fx:Script> <fx:Declarations> <s:RemoteObject id="servEstado" destination="estadoServices" showBusyCursor="true"> <s:method name="save" fault="faultHandler(event)" result="estadoSaveResultHandler(event)"/> <s:method name="update" fault="faultHandler(event)" result="estadoUpdateResultHandler(event)"/> <s:method name="delete" fault="faultHandler(event)" result="estadoDeleteResultHandler(event)"/> <s:method name="listAll" fault="faultHandler(event)" result="estadoListAllResultHandler(event)"/> </s:RemoteObject> <s:RemoteObject id="servCidade" destination="cidadeServices" showBusyCursor="true"> <s:method name="save" fault="faultHandler(event)" result="cidadeSaveResultHandler(event)"/> <s:method name="update" fault="faultHandler(event)" result="cidadeUpdateResultHandler(event)"/> <s:method name="delete" fault="faultHandler(event)" result="cidadeDeleteResultHandler(event)"/> <s:method name="listAll" fault="faultHandler(event)" result="cidadeListAllResultHandler(event)"/> </s:RemoteObject> <s:RemoteObject id="servCliente" destination="clienteServices" showBusyCursor="true"> <s:method name="save" fault="faultHandler(event)" result="clienteSaveResultHandler(event)"/> <s:method name="update" fault="faultHandler(event)" result="clienteUpdateResultHandler(event)"/> <s:method name="delete" fault="faultHandler(event)" result="clienteDeleteResultHandler(event)"/> <s:method name="listAll" fault="faultHandler(event)" result="clienteListAllResultHandler(event)"/> </s:RemoteObject> </fx:Declarations> <s:Panel x="10" y="10" width="567" height="565" title="Cadastro de Clientes"> <mx:DataGrid y="6" left="10" right="10" height="196" id="tabelaClientes" click="tabelaClientesClickHandler(event)"> <mx:columns> <mx:DataGridColumn headerText="Nome" dataField="nome"/> <mx:DataGridColumn headerText="Sobrenome" dataField="sobrenome"/> <mx:DataGridColumn headerText="CPF" dataField="cpf" width="100"/> <mx:DataGridColumn dataField="dataNascimento" headerText="Dt. Nasc" width="80" itemRenderer="DateRenderer"/> <mx:DataGridColumn dataField="cidade.nome" headerText="Cidade"/> </mx:columns> </mx:DataGrid> <mx:Form y="210" left="10" right="10" height="281"> <mx:FormItem label="ID:"> <s:TextInput width="40" enabled="false" id="fieldIdCliente"/> </mx:FormItem> <mx:FormItem label="Nome:"> <s:TextInput id="fieldNomeCliente"/> </mx:FormItem> <mx:FormItem label="Sobrenome:"> <s:TextInput id="fieldSobrenomeCliente"/> </mx:FormItem> <mx:FormItem label="CPF:"> <s:TextInput width="95" id="fieldCPFCliente"/> </mx:FormItem> <mx:FormItem label="Data de Nascimento:"> <mx:DateField id="fieldDataNascCliente" formatString="DD/MM/YYYY"/> </mx:FormItem> <mx:FormItem label="Rua:"> <s:TextInput id="fieldRuaCliente" width="249"/> </mx:FormItem> <mx:FormItem label="Número:"> <s:TextInput width="59" id="fieldNumeroCliente"/> </mx:FormItem> <mx:FormItem label="CEP:"> <s:TextInput width="94" id="fieldCEPCliente"/> </mx:FormItem> <mx:FormItem label="Cidade:"> <s:ComboBox width="200" id="comboCidadeCliente"/> </mx:FormItem> </mx:Form> <s:Button x="329" y="499" label="Novo" click="btnNovoClienteClickHandler(event)"/> <s:Button x="407" y="499" label="Salvar" click="btnSalvarClienteClickHandler(event)"/> <s:Button y="499" label="Excluir" right="10" click="btnExcluirClienteClickHandler(event)"/> </s:Panel> <s:Panel x="585" y="10" width="360" height="277" title="Cadastro de Cidades"> <mx:DataGrid x="10" y="10" id="tabelaCidades" width="338" height="106" itemClick="tabelaCidadesClickHandler(event)"> <mx:columns> <mx:DataGridColumn headerText="Nome" dataField="nome" width="180"/> <mx:DataGridColumn headerText="Estado" dataField="estado.nome" width="150"/> </mx:columns> </mx:DataGrid> <mx:Form y="120" left="10" right="10" height="83"> <mx:FormItem label="Nome:"> <s:TextInput id="fieldNomeCidade" width="189"/> </mx:FormItem> <mx:FormItem label="Estado:"> <s:ComboBox id="comboEstadoCidade"/> </mx:FormItem> </mx:Form> <s:Button x="126" y="211" label="Novo" click="btnNovoCidadeClickHandler(event)"/> <s:Button x="204" y="211" label="Salvar" click="btnSalvarCidadeClickHandler(event)"/> <s:Button x="278" y="211" label="Excluir" click="btnExcluirCidadeClickHandler(event)"/> </s:Panel> <s:Panel x="585" y="295" width="360" height="280" title="Cadastro de Estados"> <mx:DataGrid y="10" id="tabelaEstados" left="10" right="10" height="107" itemClick="tabelaEstadosClickHandler(event)"> <mx:columns> <mx:DataGridColumn headerText="Nome" dataField="nome" width="150" resizable="true"/> <mx:DataGridColumn headerText="Sigla" dataField="sigla" width="80" resizable="true"/> </mx:columns> </mx:DataGrid> <mx:Form y="121" right="11" left="9" height="86"> <mx:FormItem label="Nome:"> <s:TextInput width="187" id="fieldNomeEstado"/> </mx:FormItem> <mx:FormItem label="Sigla:"> <s:TextInput width="40" id="fieldSiglaEstado"/> </mx:FormItem> </mx:Form> <s:Button x="121" y="215" label="Novo" click="btnNovoEstadoClickHandler(event)"/> <s:Button x="199" y="215" label="Salvar" click="btnSalvarEstadoClickHandler(event)"/> <s:Button x="277" y="215" label="Excluir" click="btnExcluirEstadoClickHandler(event)"/> </s:Panel> </s:Application>
Finalmente, terminamos nosso tutorial. Outra sugestão que dou é que você incremente ainda mais o exemplo, inserindo validadores nos formulários por exemplo e arrumando algum erro que ainda exista na lógica da aplicação. Espero que tenham gostado. Qualquer dúvida, postem um comentário.
Obrigado a todos que leram e tiveram paciência de chegar até aqui 😉
Bom Dia,
Muito bom os seus posts, referentes ao flex, esclareceu muitas duvidas, afinal ainda estou meio que iniciando no Flex, o post foi muito completo, muito obrigado pela disposição em fazer um tutorial tão completo…
abraço
Oi Andrei, fico feliz que tenha gostado! Estou pensando em novos tutoriais. Espero que logo eu possa postar novidades 😉
Abraço!
David, meus parabéns pela iniciativa :]
Ta muito bom o tutorial, bem detalhado, até um macaco entende hehe 😛
To baixando o flash builder ainda mas ja li todo o tuto… muito bom mesmo!
Só vou ter que dar um jeito de fazer isso usando o FlashDevelop, pq la na empresa acho q não vão querer comprar o flash builder =\
Abraço e vlw pelo tuto 😀
Oi Duarte, fico feliz que tenha gostado 😉
Abraço!
Parabens pelo tutorial!
Gostaria de tirar algumas duvidas, ja que vc tem um bom conhecimento em flex.
Sem comparaçoes, mas comparando, gostaria de saber como ficaria a navegacao entre telas, tendo como exemplo jsf+facelets. Pois venho pesquisando e nao encontrei muita coisa a respeito.
Att.
Sergio
Oi Sérgio,
Então, no Flex vc teria apenas uma página html e as “telas” estariam todas dentro de um arquivo swf que é chamado neste html. No Flex existem algumas formas de se fazer isso. Você poderia trabalhar com os “estados”, onde vc define diversos estados para um mxml e diz para qual você quer ir no momento, ou então ter diversos arquivos mxml, cada um implementando uma janela da aplicação por exemplo (um arquivo para cadastro de Cidades, um para o de Estados e assim por diante). Então, a partir de um arquivo principal, onde vc teria um menu por exemplo, vc iria chamando cada um desses arquivos secundários como se fossem páginas html ou módulos de um programa. Eu gosto sempre de pensar na aplicação feita em Flex como uma aplicação desktop, afinal, aplicações feitas em Flex são RIAs. O JSF além de disponibilizar uma séria de componentes e os bindings entre os componentes e sua contraparte no backingbean, ele também é utilizado no controle da aplicação, ou seja, da página X vá p/ a página Y, quando o formulário Z for processado, direcione para a página W. No Flex a arquitetura é parecida com uma aplicação desktop.
[]´s
Muito obrigado pela explicação.
A minha dificuldade no momento seria apenas esta mesmo, pois tenho uma aplicação em JSF e estou analisando uma possivel alteração de front-end utlizando o flex. Fiz alguns testes com o Flex 3, e, procurando em muitos foruns, blogs, nao encontrei algo que realmente tinha um esquema de “navegação” entre paginas, encontrei apenas exemplos de uma pagina apenas que ja era chamada na aplicaçao principal. Em algus lugares encontrei a utilizaçao de Popups apenas.
Voce nao teria um exemplo para tal duvida? Pois estou muito tendencioso a utilizar o Flex. hehe..
Novamente obrigado.
[ ]’s
Oi Sérgio,
Posso até preparar algo, mas estou um pouco enrolado no momento.
Acho que de tarde dá tempo de eu fazer alguma coisa.
Abraço!
Boa tarde David!
Não é o intuito te atrapalhar.
Aproveitei uma dificuldade minha, e talvez de varios outros, para pedir uma soluçao a quem tem grande conhecimento. hehe, porem, com isso, respeitando sua disponibilidade…. hehehe
Grande abraço!!
Oi Sérgio,
Já estou preparando um tutorial novo para separar cada cadastro em uma janela diferente.
Talvez até amanhã eu consiga terminar 😉
Fique ligado no blog, logo logo tem novidade 😉
Abraço!
Oi Sérgio,
O tutorial está pronto 😉
Espero que gostem.
O próximo será sobre configuração do Flex SDK e do uso do FlashDevelop. Uma alternativa para quem não quiser usar o Flash Builder.
Abraço!
Bom dia David!
Parabéns pelo tutorial. Me esclareu muita coisa, e acho que agora embalo de vez com o flex.
Muito obriago pelo tempo dedicado.
Novamene Parabéns!!!
[ ]’s
Que bom que gostou Sérgio!
Abraço!
Onde eu posso baixar o codigo desse tutorial?
Oi Claudemir,
Como estou postando todos os códigos, passo a passo, eu acabei não publicando os projetos, visto que eles também ficam um pouco grandes e não tenho a opção de subir arquivos .zip no WordPress. Estou pensando em disponibilizar os códigos, mas ainda não decidi qual serviço de hospedagem vou usar.
Abraço!
quando executo o projeto mostra um erro dizendo que esta faltando o arquivo context.xml… o que pode ser?
o erro esta abaixo…
Deployment localizado em C:\David Buzatto\IntegracaoFlexJava\build\web
C:\David Buzatto\IntegracaoFlexJava\build\web\META-INF\context.xml (O sistema não pode encontrar o arquivo especificado)
C:\David Buzatto\IntegracaoFlexJava\nbproject\build-impl.xml:715: O módulo não foi implementado.
FALHA NA CONSTRUÇÃO (tempo total: 0 segundos)
ja refiz os passos…
Oi Álvaro,
Por padrão, o NetBeans gera o arquivo context.xml na pasta META-INF.
Você tentou criar o projeto novamente?
Se mesmo assim não funcionar, crie o arquivo context.xml dentro do META-INF e cole o código:
Abraço!
Ótimo tuto David valew mesmo cara, finalmente consegui rodar esse flex com java e blazeDS ja estava quase desinstindo e partindo pro PHP com flex mas fazendo tudo detalhado funcionou blezinha e o melhor no proprio netbeans que é a IDE que uso, agora é integrar o JPA e correr pra estudar os componentes do Flex HAuHUAHu.
Obrigado.
Oi Diogo,
Fico feliz que tenha gostado!
Abraço!
Resolvi o problema David, criei novamente o projeto e refiz os passos, agora tudo certo…
Desculpe minha falta de educacao… esqueci de agradecer, seus tutorias estao otimos, muito bem explicados, passo a passo, esclareceu muitas situacoes que eu tinha duvidas… muito bom, valeu mesmo… parabens…
Oi Álvaro,
Que bom que deu certo!
Fico feliz que tenha gostado 😉
Abraço!
Bom dia David!
Sem querer ser um pidao inveterado rsrsrsr, ai vai uma sugestao…
Qual a possibilidade de voce implementar o hibernate annotations para finalizar esse tutorial? Como vc mesmo diz no inicio, acredito que nao seja muito complicado…
acredito que para muitas pessoas seria interessante, pelo menos pra mim vai, contudo e so uma sugestao, mas desde ja agradeco…
um abraco…
Oi Álvaro,
Acho legal sua sugestão, mas sinceramente não sei se vou fazer, pq estou meio sem tempo e queria dar uma variada no blog.
Vamos ver 😉
Abraço!
Olá David
Excelente tutorial, parabéns
Só que estou com uma dúvida
Tipo nessa parte aqui
servEstado.update(estadoSelec);
Quando algum dado é atualizado no banco ele atualiza automaticamente a tabela sem código algum?
Porque não achei nenhum código pra carregar a tabela após a atualização dos dados
Att.
Oi Bruno,
Primeiramente obrigado 😉
Então, quanto a sua dúvida, o estado que está selecionado já tem o identificador então na hora do update, o estado correto é atualizado.
Quanto à atualização da tabela (grid) não há necessidade, pois perceba que obtemos o objeto e atualizamos seus campos para depois enviar os dados para o servidor.
Note que nas classes de modelo, elas estão anotadas com [Bindable]. Essa “anotação” (no flex é chamada de metadata tag), diz que os objetos instanciados a partir dessa classe, ao serem atualizados, essas atualizações devem refletir nos componenets de interface gráfica.
Procure por “flex bindable” (sem aspas) no Google para entender melhor como funciona.
Abraço!
certo
E para a criação do novo objeto?
// não tem estado selecionado, cria um novo
var o: Estado = new Estado();
// insere os valores
o.nome = fieldNomeEstado.text;
o.sigla = fieldSiglaEstado.text;
// manda salvar
servEstado.save(o);
Aqui eu não peguei objeto nenhum da grid
Criei um objeto e depois salvei e a tabela foi carregada com o objeto novo
É a mesma idéia da atualização?
Valew
achei o método que atualiza a tabela
private function estadoUpdateResultHandler( event: ResultEvent ): void {
// se entrou no método, quer dizer que o estado foi alterado, sendo assim,
// atualiza a tabela chamando o listAll e reinicia o formulário.
servEstado.listAll();
resetFormEstados();
}
mas quando esse método é chamado?
Valew
Ahhhhhhhhhhhhhhhh
Entendi agora hehehe
Eu criei os metodos no remote objet
Ae quando esse método é usado ele roda as funções que obtem o resultado
heheh
Desculpa incomodar tanto
Mais agora eu entendi
Obrigado pelo tutorial
Abraços
Bruno, a lista de objetos é obtida toda vez que se faz a atualização nos objetos.
Falei besteira, não tinha olhado o código. Nem sei o que eu pensei na hora que eu respondi.
Veja que nos tratadores do evento result dos métodos save, update e delete, é disparado o método listAll().
Mas a idéia do bindable é a que eu falei mesmo. Dê uma pesquisada 😉
[]´s
É eu vi hehehe
Obrigado
Abraços
Bom dia David!
Quero te parabenizar pelo tutorial esta me ajudando muito no desenvolvimento de um sistema para um cliente porém tenho enfrentado um problema, meu projeto não esta atualizando ou seja faço algumas alterações no flash builder e quando rodo o projeto no Netbeans o projeto não atualiza… Gostaria de saber se vc já enfrentou tal problema e se é algum tipo de bug ou se eu estou fazendo algo errado. Grato pela atenção!!!
Oi Felipe,
Fico feliz que os tutoriais estejam ajudando!
Quanto ao problema, podem ser duas coisas:
1 – O Flash Builder não está fazendo o build completo do projeto na hora que você salva algum arquivo;
2 – O navegador está usando o .swf que está em cache pq não está reconhecendo que o arquivo mudou.
Para o problema 1, basta vc clicar com o botão direito no projeto (dentro do Flash Builder) e escolher “Build Project”, ai você força a IDE a refazer todo o build. Para o problema 2, basta você segurar a tecla Shift e atualizar a página. No Firefox o atalho é Ctrl+Shift+R, no IE, vc pode segurar o Shift e apertar F5 (Shift+F5).
Veja se resolve 😉
[]´s
muito bom o tutorial, está de parabéns.
Obrigado Roberto!
[]´s
Meus parabéns pelo mega tutorial, consegui tirar muitas duvidas q tinha…
Mais estou tendo problemas no mapeamento de objetos do java para o flex, quando um Objeto conter um outro objetos o flex não consegue mapiar os sub objetos do mesmo.
Exemplo do seu tutorial seria a Classe Cidade que contem um Objeto Estado.
Att,
Rodolpho
Oi Rodolpho,
Primeiramente obrigado! Quanto à sua dúvida, por favor, verifique novamente os passos que você fez. Se o exemplo não está funcionando é pq vc deixou algum detalhe passar desapercebido.
[]´s
Muito Excelente seu Tutorial David! Parabens!
Gostaria de tirar uma dúvida! Estou apanhando a algum tempo com relação a esta comunicação entre java e flex com RemoteObject com relação ao campo java.util.Date.
Pois quando o java deserializa isto na camada back-end a data fica diferente, com um dia a menos, achei algumas coisas a respeito, porém as pessoas só sugerem usar o atributo como String. Enfim nao concordo muito com esta solução. Gostaria de saber se sabe algo a respeito, porque no seu tutorial vc usou o atributo cliente.dataNascimento, e aparentemente nao teve problemas.
Grato!
Oi Ricardo,
Primeiramente obrigado! Que bom que gostou do tutorial 😉
Quanto a sua dúvida, achei estranho pois não tive esse problema. Eu me lembro de algo parecido, quando em um dos lados um determinado atributo (não lembro qual) iniciava em 0 enquanto do outro lado iniciava em 1, mas dei uma pesquisada e não é o dia do mês. Vou procurar algo sobre o assunto, se encontrar eu lhe respondo novamente. Se você encontrar antes, compartilhe comigo e com os outros leitores ok?
Muito obrigado!
Abraço!
Olá David!
Só para registrar pra galera sobre meu problema, acho que encontrei uma solução entre aspas…rsrsrs o que ocorre é o seguinte: Se eu abrir a configuração de fuso horário do Windows e colocar pra ele ajustar automáticamente para o horário de verão, que no brasil não tem data certa pra começar e terminar. Ele apresenta o problema e começa a chegar a hora e data de forma errada no java. Pode fazer o teste aqui no Mato Grosso por exemplo é GMT -4 porem no horário de veráo é -3 porem o nem o flex e nem o java consideram isto. Enfim, encontrei uma solução neste link http://www.jandersonfc.com/tutorial-java-flex-na-pratica-8-datas/.
Porém não me resolveu muita coisa porque neste caso ai ele especifica somente Datas e eu trabalho com campo Time também. Enfim espero que isto possa ajudar alguem, e se alguem tiver alguma consideração vou estar acompanhando!
Grato!
Realmente Ricardo, se o SO estiver configurado para atualizar a data do horário de verão, existe o “problema” da hora ficar diferente. Isso pq a hora na verdade mantém-se inalterada por dentro do SO.
[]´s
David, é impressionante a sua capacidade de transformar conteudos complexos em explicaçoes acessíveis aos iniciantes numa matéria. Muitos especialistas parecem descrever tutoriais assumindo que o leitor já conhece muito, e deixa de passar determinadas explicações que levam o leitor até a abandonar o tutorial. E aí voce faz a diferença, perdendo inclusive o seu tempo com comentarios que outros ignorariam.
Mais uma vez parabéns (já havia me manifestado nas partes 1 e 2 do seu tutorial).
Gostaria de lhe fazer uma pergunta : com base neste seu excelente tutorial (pelo que voce já explicou em relação ao BLAZEDS), voce vê como possível nós, seus leitores, desenvolvermos um projeto no NetBeans integrando JavaFX BlazeDS EJB (ou CDI, ou Spring ?)
Não estou encontrando nada realmente significativo na pesquisa pelo Google. Quando o Google exibe um link que cita o JavaFX, ele acaba se dedicando, por dentro, apenas ao Flex.
Mais uma vez, muito obrigado.
[]’s
Oi Paulo,
Muito obrigado novamente! 🙂
Quanto à sua dúvida, você tem que ter em mente que o BlazeDS é um “framework” que implementa o protocolo AMF, que por sua vez é usado pelo Flash Player para se comunicar com outras tecnologias, então, a integração JavaFX com alguma coisa não precisa, nem deve, utilizar a camada do BlazeDSD. Outro detalhe. O JavaFX Script (a linguagem) foi descontinuado, então não recomendo que você continue a estudar essa tecnologia. Sei que ele continuará a ser utilizado e desenvolvido como parte interna do JDK, mas não mais como uma linguagem de programação.
[]’s
Olá David, primeiramente, seus tutoriais são fantásticos, tenho apenas algumas dúvidas, não relacionadas a eles necessáriamente, mas que acredito que vc poderia me tirar:
1 – Como faço para alinhar componentes lado a lado tranquilamente, quantos eu quiser? (Por exemplo, um menu de botões lado a lado, horizontalmente).
2 – Eu trabalho com HTML, CSS e Java a bastante tempo: (É um mini sistema de ajuda) tenho uma textarea, onde eu abro HTMLs, pelo que notei o flex não exibe algumas coisas do HTML comum, gostaria de saber a mesma pergunta de cima, só que dentro do HTML que será exibido dentro da text area.
3 – Novamente no html, só que com o flex, eu gostaria de por um botão avançar e um voltar, óbvio que, bastaria por o proximo topico que gostaria de exibir e o anterior, com a href, mas quando faço isso, obviamente ele abre ocupando a janela toda, ou seja, não usa mais o SWF e a textarea, passa a usar simplesmente o browser, teria como eu setar pra abrir dentro da textarea, pelo html?
4 – Caso a pergunta 3 não de, como faço para ler uma variavel que venha pelo cabeçalho? por exemplo: vem http://www.aaa.com/ajuda.html?variavel=1000, gostaria de ler essa variavel que veio pelo cabeçalho dentro do flex, tem como?
Obrigado desde já!
Oi Luiz,
Primeiramente obrigado! 🙂
1 – Use um container do tipo HBox (horizontal box).
2 – Você quer um editor ou visualização html dentro do swf? Infelizmente acho que isso não é possível nativamente. Talvez exista algum componente de terceiros que você possa baixar e integrar no seiu projeto.
3 – Como falei acima, não existe o componente html que você precisa. Você teria que mostrar os dados de alguma outra forma e então controlar pelos botoões que mencionou. Você pode utilizar a classe flash.net.URLLoader para obter esses dados.
4 – Acredito que com a classe URLoader você consiga fazer isso.
[]’s
Ola David, ja fiz o seu tuto e agora estou partindo para integração com Spring Hibernate e na parte do flex com swiz, mas tenho uma duvida na questao de visualizaçao de excessoes no flex, teria como eu ativar algum tipo de log de erros, pois fiz varios exemplos e a aplicação para quando simplesmente eu instancio um objeto entity com Bindable, ae tem de sair revisando todo programa, existe uma forma mais eficiente para ver no console os problemas?
Oi Diogo,
Sinceramente não sei 😦
Se eu descobrir algo eu te aviso.
[]’s
Acabei encontrando o problema na classe entity no flex o RemoteClass estava faltando um = depois do alias, o pior é que no flash builder nao me mostrou nada de erro de compilação e nenhuma exception, nao é possivel que tem de programar as cegas…hehe
Tambem estou tentando descobrir algo, se descobrir comento aqui.
Obrigado.
Oi Diogo,
Realmente, tem algumas coisas que precisa prestar atenção mesmo.
Que bom que resolveu seu problema.
[]’s
Chefe, meu grid de clientes nao está aparecendo nada. posso ate ver que a array collection traz tres itens que é a qtde de clientes no banco de dados, pois percebo que as 3 primeiras linhas do grid ficam navegaveis. mas nao me aparece dado algum… o que pode ser?
Abraços…
Agnaldo Carvalho.
Oi Agnaldo,
Então, provavelmente você está tenando obter propriedades que não foram configuradas na classe. Reveja os tutoriais, provavelmente você perdeu algum detalhe.
[]’s
Ola David acompanho todos os seus tutoriais, expecialmente os de flex, mas agora tentando fazer a integracao com o Spring + Hibernate me deparei com o problema do LazyLoad, andei pesquisando e vi que o GraniteDS funciona melhor que o BlazeDS, estou tentando seguir os tuto do proprio site mas ta meio complicado, vi em uma msg no guj de 2008 que voce ia fazer um tutorial sobre o GraniteDS chegou a fazer?
Obrigado.
Oi Diogo,
Infelizmente não escrevi nenhum tutorial sobre o Granite. Na época, se não me engano, ainda não existia o Blaze. Quanto ao Granite, que faz muito tempo que usei, acreditoque a seja configurado da mesma forma que o Blaze, entretanto com a parte de lazy loading eu nunca mexi pq não precisei.
[]’s
mas como voce faz para integrar Hibernate com BlazeDS sem dar o problema no Lazy?
Eu nunca usei o Blaze ou o Granite com Hibernate, pois nunca fiz um projeto real com as tecnologias 🙂
[]’s
Cara, meus parabéns, esse seu tuto tá o bicho!
Oi Rener,
Obrigado!
[]’s
Parabéns David, tutos iguais aos seus não se encontram por ai.
caro colega, não querendo abusar, mas já abusando, estou com grandes dificuldades em lidar com o componente CheckBox corretamente. Você pode me ajudar?
exemplo 1: em um formulário de cadastro de clientes consta um checkbox que tem por finalidade “inativar” o registro, portanto quando este for marcado deve gravar no banco de dados (1) para true e (0) false. Com isso poderei controlar outros eventos como deletar, que se o cliente estiver inativo não poderá ser excluído e assim por diante.
exemplo 2: em um datagrid cada registro representa uma imagem e em cada linha tem um checkbox, quando o usuário marca 3 registros o sistema tem que saber quem foi selecionado e enviar essas imagens para um lugar determinado…
detalhe David, sei que seus tutos são em java, eu estou usando php, pode me ajudar?
abraço e parabéns
Oi Cristiano,
Obrigado pelo elogio. 😉
Quanto à dúvida, basta registrar o evento “click” no checkbox. O manipulador deve verificar se o check foi checado ou não e então fazer o que for preciso. Segue o código básico do manipulador.
private function clickHandler(event:MouseEvent): void {
if ( event.target.selected ) {
// se foi checado...
} else {
// se foi "deschecado"...
}
}
Note que não indentei o código pq infelizmente não dá p/ fazer isso no comentário. 😦
Quanto à sua segunda dúvida, eu nunca precisei fazer algo do tipo, mas basta vc percorrer o grid e verificar o que está ou não selecionado e então enviar os dados. Em relação ao PHP, se vc quiser usar o RemoteObject, vc vai precisar usar alguma implementação do protocolo. Basta dar uma pesquisada no Google.
[]’s
David, excelente publicação.
Fiz tudo conforme o tutorial, esclareceu-me muita coisa e a aplicação tá rodando perfeitamente aqui.
Parabéns!
Obrigado Tiago!
[]’s
Cara muito Obrigado por esse tutorial !
Estou iniciando agora no Flex e isso me ajudou muito !
Valeu !
De nada Felipe!
Fico feliz que os tutoriais tenham lhe ajudado!
[]’s
Olá denovo,
vi que há a opção de gerar as classes automáticamente pelo blazeDS.
O que você acha melhor, gerar as classes na mão como você fez ou utilizar o blaze ?
Há você já teve esse erro -> Unable to resolve resource bundle “fiber” for locale “pt_BR”. ?
Nunca tive esse problema. 😦
Vc procurou no Google para ver se existe alguma solução?
[]’s
Olá novamente,
Vi que o blazeDS pode gerar as classes automaticamente no flex, porém gera tudo muito confuso.
O que você recomenda ? Criar na mão como você fez ou utilizar o recurso do blaze ?
Grato.
Felipe, eu prefiro código feito na mão na maioria das vezes, pq normalmente fica mais limpo, mas você pode usar as classes geradas sem problema.
[]’s
Muito bom seus tutoriais David, me tirou mtas dúvidas já q sou iniciante em flex.
Mto Obrigada!!
Até.. 😀
Oi Juliane,
Que bom que os tutoriais lhe ajudaram. 😉
[]’s
Meus parabéns David é bom saber que existe pessoas como você, encontrei muitos tutoriais pela internet tanto em português quanto em inglês e nenhum foi tão bem explicado quanto o seu. Consegui realizar todas as etapas e aprendi muito com elas. Você tem algum email para contato para tirar possiveis duvidas que surgirão?
Oi Misael,
Que bom que gostou!
Quanto a dúvidas, recomendo que você se inscreva em algum fórum.
De Java eu recomendo o http://www.guj.com.br
De Flex tem o http://forum.flexbrasil.com.br/ e os fóruns da própria Adobe.
O canal de comunicação aqui no blog é pelas mensagens mesmo. Eu evito responder dúvidas por aqui também, pois infelizmente não tenho tempo de respondê-las.
[]’s
Hello David, Realmente e definitivamente o seu tutorial “matou a pau”, muito completo [PARABÉNS] o mundo web precisa de caras assim como você que tem como propósito passar o conhecimento.
Abraço!
Márcio Zonta
Obrigado Márcio 😉
[]’s