Olá a todos. Estive um pouco sumido nos últimos dias. Criar esses tutoriais detalhados demanda um trabalho considerável sendo preciso ter coragem (e tempo) para fazer hehe. O Sérgio, leitor do Blog, me pediu algumas dicas em como estruturar uma aplicação feita no Flex em janelas. Pois bem, decidi então fazer um tutorial sobre isso. Para o tutorial, iremos utilizar como base o tutorial anterior e quem ainda não o leu, pode iniciar pela Parte 1.

Vamos começar então. Primeiro, vamos fazer uma cópia do nosso projeto do frontend. Para isso, no Flash Builder, procure pelo projeto “IntegracaoFlexJavaGUI”, clique com o botão direito do mouse na raiz do projeto e clique em “Copy”. Veja a Figura abaixo.

 

Figura 1

Copiando o projeto

 

Com o projeto copiado, agora basta colar. Para isso, clique em algum lugar fora do projeto original e escolha “Paste”. Veja a Figura abaixo.

 

Figura 2

Colando o projeto

 

Quando você clicar na opção “Paste”, a janela “Copy Project” vai aparecer. No campo “Project Name” vamos preencher com o nome “IntegracaoFlexJavaGUIComJanelas”. Desmarque a opção “Use default location” e no campo “Location” localize a pasta onde estamos salvando nossos projetos. No meu caso, estou salvando tudo na pasta “C:\Users\David\Documents\Java\Flex”. Tome o cuidado de indicar que você quer que uma pasta para o projeto seja criada, colocando no final do caminho o nome do projeto. No meu caso, o valor completo do campo “Location” ficou “C:\Users\David\Documents\Java\Flex\IntegracaoFlexJavaGUIComJanelas” (sem as aspas). Com isso pronto, clique no OK. Veja a Figura abaixo.

 

Figura 3

Criando o novo projeto com base no anterior

 

Legal, o Flash Builder vai criar então o novo projeto. Perceba que não vamos precisar configurar o caminho do backend, pois ele já estava configurado no projeto anterior, e como fizemos uma cópia, todas aquelas propriedades que alteramos quando criamos o projeto original foram copiadas.

Agora vamos reorganizar nosso novo projeto. Cuidado para não fazer as modificações no projeto antigo dentro do Flash Builder. Clique então com o botão direito na pasta “src”, escolha New > Package. Dê o nome de “gui” (sem as aspas) para o pacote e clique em Finish. Com isso, criamos um novo pacote para o nosso projeto, onde vamos colocar nossos arquivos mxml que vão representar nossa interface gráfica e seus componentes.

Perceba que nossos mxmls ficaram todos no “default package”. Primeiramente selecione o arquivo IntegracaoFlexJavaGUI.mxml e remova-o do projeto (selecione o arquivo, tecle <DELETE>). Sobraram dois arquivos, o CRUD.mxml e o DateRenderer.mxml. Selecione os dois arquivos e os arraste para o pacote “gui” que acabamos de criar. A janela “Move” vai aparecer. Deixe apenas a opção “Update references” marcada e clique em OK. Com isso os arquivos serão movidos para o novo pacote.

Feito isso, clique agora com o botão direito no pacote “gui” e escolha New > MXML Application. Preencha o campo “Name” com “Principal” (sem aspas) e deixe o campo “Layout” como “None”. Clique em Finish. O Flash Builder não vai criar o nosso arquivo no pacote “gui”, mesmo tendo selecionado esse pacote quando clicamos com o botão direito :(. O Principal.mxml vai ser criado no default package. Da mesma forma que acabamos de fazer com os arquivos CRUD.mxml e DateRenderer.mxml, selecione o arquivo Principal.mxml e o mova para o pacote “gui”.

Perceba que o ícone do CRUD.mxml tem uma bolinha azul. Isso quer dizer que esse arquivo é o ponto de entrada da nossa aplicação, ou seja, é a partir dele que a aplicação inicia. Vamos mudar isso. Queremos que o Principal.mxml seja o ponto de entrada. Para isso, clique com o botão direito no Principal.mxml e escolha a opção “Set as Default Application”. Você vai ver que a bolinha vai passar para o Principal.mxml. Iremos manter o CRUD.mxml, pois é nele que está implementada a nossa aplicação original e vamos reaproveitar aquele código quando estivermos montando nossas janelas.

Antes de qualquer outra coisa, lembra que o resultado da compilação do Flash Builder era armazenado na pasta “swf” do nosso projeto do NetBeans? Pois bem, vá no NetBeans, procure a pasta “swf” e apague todo o seu conteúdo. Lá dentro você vai ver que tem arquivos do nosso frontend antigo que não nos interessam mais. Faltou falar uma coisa. Iremos reaproveitar o projeto do NetBeans, então não iremos criar um projeto novo. Caso você queira fazer isso, o processo é parecido com o do Flash Builder. Só não fiz isso, pois senão o caminho do projeto do backend iria mudar e então teríamos que configurar mais coisas no nosso projeto no Flash Builder, mudando o caminho do backend. Sendo assim, sugiro que mantenha o projeto antigo do NetBeans.

Ainda no projeto do NetBeans, abra o index.jsp e agora no <meta … > mude a url que define o redirecionamento para o CRUD.html, colocando no lugar o Principal.html (que é o arquivo HTML que vai ser gerado para conter nosso projeto). O <meta … > do index.jsp vai ficar assim:

Alteração da tag <meta … >

<meta http-equiv="Refresh" content="0; url=swf/Principal.html">

Rode o projeto do NetBeans. Uma página em branco deve aparecer, afinal, ainda não colocamos nada no nosso Principal.mxml. Não precisaremos mais mexer no NetBeans, só iremos utilizá-lo agora para executar nosso projeto quando formos fazendo alterações na interface gráfica. Lembre-se, nossa infraestrutura de persistência e de serviços já está pronta!

Vamos então à montagem da nossa interface gráfica. Para isso, no Flash Builder, abra o Principal.mxml e vá na aba Design. Na aba dos componentes (canto inferior esquerdo) procure pelo grupo “Navigators”. Dento desse grupo existe o componente “MenuBar”. Clique e arraste um MenuBar para o Principal.mxml. A aparência da barra de menu, por enquanto, é igual a de um botão, mas sem nenhum texto.Veja a Figura abaixo.

 

Figura 4

Arrastando um componente MenuBar

 

Queremos então que esse menu ocupe toda a parte acima da nossa aplicação. Para isso, arraste o componente até o canto superior esquerdo do Principal.mxml. Veja a Figura abaixo.

 

Figura 5

Posicionando o componente MenuBar

 

Feito isso, selecione o componente (clique nele) caso ainda não esteja selecionado. No canto inferior direito do Flash Buillder, na aba “Properties” podemos alterar as propriedades do componente. Iremos utilizar a visualização padrão das propriedades (Standard View), que nos fornece um editor amigável para as propriedades mais comuns do componente selecionado no momento. Veja a Figura abaixo. O botão para acessar as propriedades padrão está destacado.

 

Figura 6

Propriedades padrão do componente selecionado

 

No painel de propriedades, com o MeuBar selecionado, procure pela seção “Size and Position”. X e Y devem estar com 0, pois colocamos nosso componente no canto superior (Y=0) esquerdo (X=0) lembram? Agora vamos usar duas constrains para esticar a barra de menu, fazendo ela ocupar toda a interface horizontalmente. Veja a Figura abaixo.

 

Figura 7

Usando as constrains left e right para esticar o componente MenuBar

 

Veja que quando você modificar essas propriedades, a barra de menu vai passar a ocupar toda a interface na horizontal. Note também que com isso, a propriedade X ficou sem um valor. Isso porque quando especificamos o left (checkbox à esquerda) como 0 e o right (checkbox à direita) como 0 também (campos abaixo) não precisamos mais dizer onde o componente está na posição X, pois ele vai preencher horizontalmente seu container, no casso, o Principal.mxml (que é do tipo s:Application).

Vamos agora inserir os itens no nossa barra de menu. Infelizmente não temos uma opção visual para isso, então temos que fazer isso diretamente no código. Para isso, vá na opção “Source” do Principal.mxml e localize o seu MenuBar (<mx:MenuBar>).

Altere o código para isso aqui:

Código do componente MenuBar

<mx:MenuBar y="0" left="0" right="0" labelField="@label">
	<fx:XMLList>
		<menuitem label="Cadastros">
			<menuitem id="itemCadClientes" label="Clientes"/>
			<menuitem type="separator"/>
			<menuitem id="itemCadCidades" label="Cidades"/>
			<menuitem id="itemCadEstados" label="Estados"/>
		</menuitem>
	</fx:XMLList>
</mx:MenuBar>

Parece complicado, mas não é. Vamos primeiro detalhar o conteúdo da tag <mx:Menu>. Perceba que dentro do nosso menu, declaramos uma estrutura de dados chamada XMLList. Basicamente essa estrutura utiliza código XML para estruturar dados. Note que dentro das tags <fx:XMLList> e </fx:XMLList> temos o código XML que será utilizado para estruturar o nosso menu. Temos então um item para o cadastro, representado pela tag <menuitem> mais externa. Note que essa tag tem uma propriedade chamada “label”. Essa propriedade será utilizada pelo renderizador da barra de menus para desenhar o nome de cada botão. Note na tag <mx:MenuBar> o uso da propriedade “labelField”, onde usamos o valor “@label”. Isso significa que queremos usar o atributo “label” das tags definidas no XML como o label (rótulo) dos nossos itens do menu. O sinal de arroba (@) indica que “label” é um atributo. Dentro então da tag <menuitem> mais externa, temos então 4 outros itens, um para cada cadastro e um para o separador do menu (type=”separator”). Note que podemos ir aninhando tags do tipo <menuitem> para irmos construindo nosso menu.

Para ver as mudanças, dê o build no projeto (botão direito na raiz do projeto > Build Project) e execute a aplicação pelo NetBeans. Legal, o menu funciona. Agora falta o principal, ou seja, adicionar interação nos itens do menu, pois queremos que algo aconteça quando um dos itens do cadastro sejam clicados. Para isso iremos registrar um ouvinte na barra de menu, e na implementação do método ouvinte, verificamos qual foi o item clicado com base no id do item. Segue então o código completo do Principal.mxml.

gui.Principal.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">

	<fx:Script>
		<![CDATA[
			import mx.controls.Alert;
			import mx.events.MenuEvent;

			private function menuItemClickHandler( event: MenuEvent ): void {

				// faz o switch baseado no id do item selecionado
				switch ( event.item.@id.toString() ) {

					case "itemCadClientes":
						Alert.show( "Botão Clientes clicado!", "Mensagem" );
						break;

					case "itemCadCidades":
						Alert.show( "Botão Cidades clicado!", "Mensagem" );
						break;

					case "itemCadEstados":
						Alert.show( "Botão Estados clicado!", "Mensagem" );
						break;

				}

			}

		]]>
	</fx:Script>

	<fx:Declarations>
		<!-- Place non-visual  elements (e.g., services, value objects) here -->
	</fx:Declarations>

	<mx:MenuBar y="0" left="0" right="0" labelField="@label" itemClick="menuItemClickHandler(event)">
		<fx:XMLList>
			<menuitem label="Cadastros">
				<menuitem id="itemCadClientes" label="Clientes"/>
				<menuitem type="separator"/>
				<menuitem id="itemCadCidades" label="Cidades"/>
				<menuitem id="itemCadEstados" label="Estados"/>
			</menuitem>
		</fx:XMLList>
	</mx:MenuBar>

</s:Application>

Teste o projeto. Verifique que agora, baseado no item clicado no menu, um Alert diferente é mostrado. Vamos agora implementar nossa primeira janela. Quando tivermos ela pronta, iremos chamá-la pelo botão correspondente. Iniciaremos pela janela de cadastro de Estados.

Para isso, vamos criar um componente mxml, mas antes um pouco de teoria. Quando criamos arquivos MXML, na verdade estamos estendendo classes do ActionScript. Quando criamos uma aplicação MXML (MXML Application), você pode notar que o arquivo fonte é escrito em XML e que a tag raiz (root) é do tipo <s:Application>. Durante a compilação, o que o Flex faz é converter esse arquivo MXML em uma classe e então o compila. Sendo assim, cada arquivo MXML vira uma classe e no final e estamos estendendo uma classe. Imagine então o nosso arquivo Principal.mxml. Na verdade, ele vira uma classe mais ou menos assim:

package gui {

	import spark.components.Application;

	public class Principal extends Application {

		// implementação...

	}

}

Para cada uma das nossas janelas, nós vamos estender a classe “TitleWindow” do pacote spark.components, mas nós vamos seguir a estratégia de criar arquivos MXML da mesma forma que estamos fazendo para as aplicações. Então vamos lá. Começaremos pela tela de cadastro de Estados. Clique com o botão direito no pacote “gui” e escolha New >MXML Component. Na tela New MXML Component, preencha o campo “Name” com “CadastroEstados” (sem as aspas), deixe o campo “Layout” como “None” e por fim, preencha o “Based on” com “spark.components.TitleWindow” (semas as aspas). Por fim, clique em Finish e arquivo do novo componente será aberto. Deixa e largura e a altura da forma que estão. Veja a Figura abaixo.

 

Figura 8

Criando um novo componente para o cadastro de Estados

 

Com o arquivo CadastroEstados.mxml aberto, vá na aba Design e vamos fazer as primeiras modificações na nossa tela de cadastro. Primeiro dê um título para a janela (pelas propriedades ou clicando duas vezes na barra de título). Sugiro “Cadastro de Estados” (sem as aspas). Com isso feito, aumente um pouco a tela para termos espaço para trabalhar. Basta redimensionar, como faria com uma imagem. No final, teremos algo assim:

 

Figura 9

Preparando a janela de cadastro

 

Agora vamos ao componentes da tela de cadastro. O bom é que já temos tudo pronto :). Abra o arquivo CRUD.mxml, vá em Design, selecione todos os componentes do painel de cadastro de estados, clique bom o botão direito e escolha “Copy”. Veja a Figura.

 

Figura 10

Copiando componentes

 

Agora volte no CadastroEstados.mxml, e em Design, clique com o botão direito na tela e escolha “Paste”. Os componentes serão colados. Organize eles e redimensione a janela. O resultado você pode ver na Figura abaixo, e o código parcial do CadastroEstados.mxml pode ser visto logo em seguinda.

 

Figura 11

Design da tela de cadastro de Estados pronto

 

gui.CadastroEstados.mxml

<?xml version="1.0" encoding="utf-8"?>
<s:TitleWindow xmlns:fx="http://ns.adobe.com/mxml/2009"
			   xmlns:s="library://ns.adobe.com/flex/spark"
			   xmlns:mx="library://ns.adobe.com/flex/mx" width="296" height="278" title="Cadastro de Estados">

	<fx:Declarations>
		<!-- Place non-visual elements (e.g., services, value objects) here -->
	</fx:Declarations>

	<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="10" left="10" 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 y="215" label="Novo" click="btnNovoEstadoClickHandler(event)" right="162"/>
	<s:Button y="215" label="Salvar" click="btnSalvarEstadoClickHandler(event)" right="84"/>
	<s:Button y="215" label="Excluir" click="btnExcluirEstadoClickHandler(event)" right="6"/>
</s:TitleWindow>

Além da parte gráfica, precisamos agora copiar o código dos manipuladores de eventos, dos serviços, etc. Como você já tem alguma experiência com o funcionamento, pois provavelmente já acompanhou o tutorial anterior, estou postando o código completo do CasdatroEstados.mxml com todo o código da interação implementado.

gui.CadastroEstados.mxml

<?xml version="1.0" encoding="utf-8"?>
<s:TitleWindow xmlns:fx="http://ns.adobe.com/mxml/2009"
			   xmlns:s="library://ns.adobe.com/flex/spark"
			   xmlns:mx="library://ns.adobe.com/flex/mx" width="296" height="278" title="Cadastro de Estados"
			   creationComplete="creationCompleteHandler(event)">

	<fx:Script>
		<![CDATA[
			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;

			private var estadoSelec: Estado;

			private function faultHandler( event: FaultEvent ): void {
				Alert.show( event.fault.toString(), "ERRO" );
			}

			private function estadoSaveResultHandler( event: ResultEvent ): void {
				servEstado.listAll();
				resetFormEstados();
			}

			private function estadoUpdateResultHandler( event: ResultEvent ): void {
				servEstado.listAll();
				resetFormEstados();
			}

			private function estadoDeleteResultHandler( event: ResultEvent ): void {
				servEstado.listAll();
				resetFormEstados();
			}

			private function estadoListAllResultHandler( event: ResultEvent ): void {
				tabelaEstados.dataProvider = event.result;
			}

			private function btnNovoEstadoClickHandler( event: MouseEvent ): void {
				resetFormEstados();
			}

			private function btnSalvarEstadoClickHandler( event: MouseEvent ): void {

				if ( estadoSelec ) {

					estadoSelec.nome = fieldNomeEstado.text;
					estadoSelec.sigla = fieldSiglaEstado.text;

					servEstado.update(estadoSelec);

				} else {

					var o: Estado = new Estado();

					o.nome = fieldNomeEstado.text;
					o.sigla = fieldSiglaEstado.text;

					servEstado.save(o);

				}
			}

			private function btnExcluirEstadoClickHandler( event: MouseEvent ): void {

				if ( estadoSelec ) {
					servEstado.getOperation( "delete" ).send( estadoSelec );
				}

			}

			private function tabelaEstadosClickHandler( event: ListEvent ): void {

				estadoSelec = tabelaEstados.selectedItem as Estado;

				fieldNomeEstado.text = estadoSelec.nome;
				fieldSiglaEstado.text = estadoSelec.sigla;

			}

			private function resetFormEstados(): void {

				fieldNomeEstado.text = "";
				fieldSiglaEstado.text = "";

				estadoSelec = null;

			}

			private function creationCompleteHandler( event: FlexEvent ): void {
				servEstado.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>

	</fx:Declarations>

	<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="10" left="10" 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 y="215" label="Novo" click="btnNovoEstadoClickHandler(event)" right="162"/>
	<s:Button y="215" label="Salvar" click="btnSalvarEstadoClickHandler(event)" right="84"/>
	<s:Button y="215" label="Excluir" click="btnExcluirEstadoClickHandler(event)" right="6"/>
</s:TitleWindow>

Legal,  já temos nossa tela de cadastro pronta (quase na verdade). Vamos então aprender como fazer para abrir essa janela de cadastro a partir do menu do Principal.mxml. Usaremos para isso a classe PopUpManager do pacote mx.managers. Essa classe possui dois métodos estáticos, o addPopUp e o removePopUp. Esses métodos são usados respectivamente para adicionar uma janela em um container e para remover uma janela de um conteiner. Vamos então modificar o switch do Principal.mxml onde tratamos os eventos dos itens dos botões. Segue então o código do Principal.mxml atualizado e comentado.

gui.Principal.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">

	<fx:Script>
		<![CDATA[
			import mx.controls.Alert;
			import mx.events.MenuEvent;
			import mx.managers.PopUpManager;

			private function menuItemClickHandler( event: MenuEvent ): void {

				// faz o switch baseado no id do item selecionado
				switch ( event.item.@id.toString() ) {

					case "itemCadClientes":
						Alert.show( "Botão Clientes clicado!", "Mensagem" );
						break;

					case "itemCadCidades":
						Alert.show( "Botão Cidades clicado!", "Mensagem" );
						break;

					case "itemCadEstados":

						// instanciamos um objeto do tipo CadastroEstados
						var cadastroEstados: CadastroEstados = new CadastroEstados();

						/*
						Usa-se a classe PopUpManager para abrir a janela.
						O primeiro parâmetro é o componente a ser aberto, o segundo é
						onde ele estará contido e o terceiro é um boolean que diz se
						a janela deve ser modal ou não.
						*/
						PopUpManager.addPopUp( cadastroEstados, this, true );

						// centralizamos a janela
						cadastroEstados.x = ( this.width / 2 ) - ( cadastroEstados.width / 2 );
						cadastroEstados.y = ( this.height / 2 ) - ( cadastroEstados.height / 2 );

						break;

				}

			}

		]]>
	</fx:Script>

	<fx:Declarations>
		<!-- Place non-visual  elements (e.g., services, value objects) here -->
	</fx:Declarations>

	<mx:MenuBar y="0" left="0" right="0" labelField="@label" itemClick="menuItemClickHandler(event)">
		<fx:XMLList>
			<menuitem label="Cadastros">
				<menuitem id="itemCadClientes" label="Clientes"/>
				<menuitem type="separator"/>
				<menuitem id="itemCadCidades" label="Cidades"/>
				<menuitem id="itemCadEstados" label="Estados"/>
			</menuitem>
		</fx:XMLList>
	</mx:MenuBar>

</s:Application>

Teste sua aplicação. Você vai perceber que agora, quando clicar no item Estados do menu Cadastros, a janela de cadastro de Estados vai abrir. Note que ela é modal (um overlay vai esmaecer a tela principal) e que você pode arrastá-la como uma janela. O único detalhe que ainda falta é permitir que ela seja fechada. Se você clicar no botão fechar da janela, não acontecerá nada. Para implementar isso, vamos definir um método para manipular o evento “close” da janela de cadastro de Estados. Esse evento é disparado quando clicamos no botão fechar. O código do método é super simples. Ele vai chamar o método removePopUp da classe PopUpManager, passando como referência o próprio componente. Segue então o código final da janela de cadastro de Estados.

gui.CadastroEstados.mxml (final)

<?xml version="1.0" encoding="utf-8"?>
<s:TitleWindow xmlns:fx="http://ns.adobe.com/mxml/2009"
			   xmlns:s="library://ns.adobe.com/flex/spark"
			   xmlns:mx="library://ns.adobe.com/flex/mx" width="296" height="278" title="Cadastro de Estados"
			   creationComplete="creationCompleteHandler(event)"
			   close="closeHandler(event)">

	<fx:Script>
		<![CDATA[
			import entidades.Estado;

			import mx.collections.ArrayCollection;
			import mx.controls.Alert;
			import mx.events.CloseEvent;
			import mx.events.FlexEvent;
			import mx.events.ItemClickEvent;
			import mx.events.ListEvent;
			import mx.managers.PopUpManager;
			import mx.rpc.events.FaultEvent;
			import mx.rpc.events.ResultEvent;

			private var estadoSelec: Estado;

			private function faultHandler( event: FaultEvent ): void {
				Alert.show( event.fault.toString(), "ERRO" );
			}

			private function estadoSaveResultHandler( event: ResultEvent ): void {
				servEstado.listAll();
				resetFormEstados();
			}

			private function estadoUpdateResultHandler( event: ResultEvent ): void {
				servEstado.listAll();
				resetFormEstados();
			}

			private function estadoDeleteResultHandler( event: ResultEvent ): void {
				servEstado.listAll();
				resetFormEstados();
			}

			private function estadoListAllResultHandler( event: ResultEvent ): void {
				tabelaEstados.dataProvider = event.result;
			}

			private function btnNovoEstadoClickHandler( event: MouseEvent ): void {
				resetFormEstados();
			}

			private function btnSalvarEstadoClickHandler( event: MouseEvent ): void {

				if ( estadoSelec ) {

					estadoSelec.nome = fieldNomeEstado.text;
					estadoSelec.sigla = fieldSiglaEstado.text;

					servEstado.update(estadoSelec);

				} else {

					var o: Estado = new Estado();

					o.nome = fieldNomeEstado.text;
					o.sigla = fieldSiglaEstado.text;

					servEstado.save(o);

				}
			}

			private function btnExcluirEstadoClickHandler( event: MouseEvent ): void {

				if ( estadoSelec ) {
					servEstado.getOperation( "delete" ).send( estadoSelec );
				}

			}

			private function tabelaEstadosClickHandler( event: ListEvent ): void {

				estadoSelec = tabelaEstados.selectedItem as Estado;

				fieldNomeEstado.text = estadoSelec.nome;
				fieldSiglaEstado.text = estadoSelec.sigla;

			}

			private function resetFormEstados(): void {

				fieldNomeEstado.text = "";
				fieldSiglaEstado.text = "";

				estadoSelec = null;

			}

			private function creationCompleteHandler( event: FlexEvent ): void {
				servEstado.listAll();
			}

			private function closeHandler( event: CloseEvent ): void {
				PopUpManager.removePopUp(this);
			}

		]]>
	</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>

	</fx:Declarations>

	<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="10" left="10" 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 y="215" label="Novo" click="btnNovoEstadoClickHandler(event)" right="162"/>
	<s:Button y="215" label="Salvar" click="btnSalvarEstadoClickHandler(event)" right="84"/>
	<s:Button y="215" label="Excluir" click="btnExcluirEstadoClickHandler(event)" right="6"/>
</s:TitleWindow>

Teste e perceba que agora a janela é fechada, e o foco retorna para a tela principal da aplicação. Muito bem. Agora como exercício, faça o mesmo processo para as outras duas telas. Copie os componentes, organize-os, copie o código dos manipuladores de eventos, dos serviços, remova algum código que esteja sobrando e altere o Principal.mxml para abrir as janelas restantes. Note que para as janelas de cadastro de Cidades e de Clientes você precisará de outros serviços além do da entidade tratada na janela. Por exemplo, no cadastro de Cidades, você precisará do serviço de estados também (o combo de estados, lembra?). Não se esqueça do evento close das janelas. Para finalizar, seguem os códigos finais das janelas de cadastro de Cidades e de Clientes o código final do Principal.mxml. Agora você pode apagar o CRUD.mxml, pois já separamos todo o seu código nas nossas telas.

gui.CadastroCidades.mxml (final)

<?xml version="1.0" encoding="utf-8"?>
<s:TitleWindow xmlns:fx="http://ns.adobe.com/mxml/2009"
			   xmlns:s="library://ns.adobe.com/flex/spark"
			   xmlns:mx="library://ns.adobe.com/flex/mx" width="304" height="272" title="Cadastro de Cidades"
			   creationComplete="creationCompleteHandler(event)"
			   close="closeHandler(event)">

	<fx:Script>
		<![CDATA[
			import entidades.Cidade;
			import entidades.Estado;

			import mx.collections.ArrayCollection;
			import mx.controls.Alert;
			import mx.events.CloseEvent;
			import mx.events.FlexEvent;
			import mx.events.ItemClickEvent;
			import mx.events.ListEvent;
			import mx.managers.PopUpManager;
			import mx.rpc.events.FaultEvent;
			import mx.rpc.events.ResultEvent;

			private var cidadeSelec: Cidade;

			private function faultHandler( event: FaultEvent ): void {
				Alert.show( event.fault.toString(), "ERRO" );
			}

			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;
			}

			private function estadoListAllResultHandler( event: ResultEvent ): void {
				comboEstadoCidade.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;

			}

			private function creationCompleteHandler( event: FlexEvent ): void {
				servCidade.listAll();
				servEstado.listAll();
			}

			private function closeHandler( event: CloseEvent ): void {
				PopUpManager.removePopUp(this);
			}

		]]>
	</fx:Script>

	<fx:Declarations>

		<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="servEstado"
			destination="estadoServices"
			showBusyCursor="true">

			<s:method
				name="listAll"
				fault="faultHandler(event)"
				result="estadoListAllResultHandler(event)"/>

		</s:RemoteObject>

	</fx:Declarations>

	<mx:DataGrid y="10" id="tabelaCidades" height="106" itemClick="tabelaCidadesClickHandler(event)" left="10" right="10">
		<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 y="211" label="Novo" click="btnNovoCidadeClickHandler(event)" right="166"/>
	<s:Button y="211" label="Salvar" click="btnSalvarCidadeClickHandler(event)" right="88"/>
	<s:Button y="211" label="Excluir" click="btnExcluirCidadeClickHandler(event)" right="10"/>
</s:TitleWindow>

gui.CadastroClientes.mxml (final)

<?xml version="1.0" encoding="utf-8"?>
<s:TitleWindow xmlns:fx="http://ns.adobe.com/mxml/2009"
			   xmlns:s="library://ns.adobe.com/flex/spark"
			   xmlns:mx="library://ns.adobe.com/flex/mx" width="484" height="562" title="Cadastro de Clientes"
			   creationComplete="creationCompleteHandler(event)"
			   close="closeHandler(event)">

	<fx:Script>
		<![CDATA[
			import entidades.Cidade;
			import entidades.Cliente;

			import mx.collections.ArrayCollection;
			import mx.controls.Alert;
			import mx.events.CloseEvent;
			import mx.events.FlexEvent;
			import mx.events.ItemClickEvent;
			import mx.events.ListEvent;
			import mx.managers.PopUpManager;
			import mx.rpc.events.FaultEvent;
			import mx.rpc.events.ResultEvent;

			private var clienteSelec: Cliente;

			private function faultHandler( event: FaultEvent ): void {
				Alert.show( event.fault.toString(), "ERRO" );
			}

			private function cidadeListAllResultHandler( event: ResultEvent ): void {
				comboCidadeCliente.dataProvider = event.result as ArrayCollection;
			}

			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;

			}

			private function creationCompleteHandler( event: FlexEvent ): void {
				servCidade.listAll();
				servCliente.listAll();
			}

			private function closeHandler( event: CloseEvent ): void {
				PopUpManager.removePopUp(this);
			}

		]]>
	</fx:Script>

	<fx:Declarations>

		<s:RemoteObject
			id="servCidade"
			destination="cidadeServices"
			showBusyCursor="true">

			<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>

	<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="gui.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 y="498" label="Novo" click="btnNovoClienteClickHandler(event)" right="166"/>
	<s:Button y="498" label="Salvar" click="btnSalvarClienteClickHandler(event)" right="88"/>
	<s:Button y="498" label="Excluir" right="10" click="btnExcluirClienteClickHandler(event)"/>
</s:TitleWindow>

gui.Principal.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">

	<fx:Script>
		<![CDATA[
			import mx.controls.Alert;
			import mx.events.MenuEvent;
			import mx.managers.PopUpManager;

			private function menuItemClickHandler( event: MenuEvent ): void {

				// faz o switch baseado no id do item selecionado
				switch ( event.item.@id.toString() ) {

					case "itemCadClientes":
						var cadastroClientes: CadastroClientes = new CadastroClientes();
						PopUpManager.addPopUp( cadastroClientes, this, true );
						cadastroClientes.x = ( this.width / 2 ) - ( cadastroClientes.width / 2 );
						cadastroClientes.y = ( this.height / 2 ) - ( cadastroClientes.height / 2 );
						break;

					case "itemCadCidades":
						var cadastroCidades: CadastroCidades = new CadastroCidades();
						PopUpManager.addPopUp( cadastroCidades, this, true );
						cadastroCidades.x = ( this.width / 2 ) - ( cadastroCidades.width / 2 );
						cadastroCidades.y = ( this.height / 2 ) - ( cadastroCidades.height / 2 );
						break;

					case "itemCadEstados":

						// instanciamos um objeto do tipo CadastroEstados
						var cadastroEstados: CadastroEstados = new CadastroEstados();

						/*
						Usa-se a classe PopUpManager para abrir a janela.
						O primeiro parâmetro é o componente a ser aberto, o segundo é
						onde ele estará contido e o terceiro é um boolean que diz se
						a janela deve ser modal ou não.
						*/
						PopUpManager.addPopUp( cadastroEstados, this, true );

						// centralizamos a janela
						cadastroEstados.x = ( this.width / 2 ) - ( cadastroEstados.width / 2 );
						cadastroEstados.y = ( this.height / 2 ) - ( cadastroEstados.height / 2 );

						break;

				}

			}

		]]>
	</fx:Script>

	<fx:Declarations>
		<!-- Place non-visual  elements (e.g., services, value objects) here -->
	</fx:Declarations>

	<mx:MenuBar y="0" left="0" right="0" labelField="@label" itemClick="menuItemClickHandler(event)">
		<fx:XMLList>
			<menuitem label="Cadastros">
				<menuitem id="itemCadClientes" label="Clientes"/>
				<menuitem type="separator"/>
				<menuitem id="itemCadCidades" label="Cidades"/>
				<menuitem id="itemCadEstados" label="Estados"/>
			</menuitem>
		</fx:XMLList>
	</mx:MenuBar>

</s:Application>

Com isso terminamos nosso tutorial onde aprendemos a separar nossa aplicação em janelas, fazendo com que ela fique mais modularizada. Como exercício você ainda pode melhorar ainda mais cada cadastro, adicionando validadores aos formulários, criando um método para centralizar as janelas na janela principal, etc. Espero que tenham gostado!

Até a próxima 😉

Anúncios