Parte 1Parte 2Parte 3Parte 4.1Parte 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:

 

Figura 1

Estrutura do projeto no NetBeans

 

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>

 

Figura 2

Estrutura da interface

 

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…”.

 

Figura 3

Acessando as propriedades das colunas

 

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…”.

 

Figura 4

Criando um renderizador para a coluna

 

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 😉

Parte 1Parte 2Parte 3Parte 4.1Parte 4.2

Anúncios