Salve galera.

Hoje vamos abordar uma forma simples de trabalhar com MVC no Flex com Eventos.

Primeiramente oque é MVC?

Com o aumento da complexidade das aplicações desenvolvidas torna-se fundamental a separação entre os dados (Model) e o layout (View). Esta forma permite que trabalhamos separadamente entre layout e manipulação de dados, tornando flexível alterações no layout sem afetar o Controller e vice-versa.

O MVC resolve um dos maiores problemas entre acoplamento das camadas de acesso a dados e lógica de negócio da aplicação e apresentação de dados ao usuário, pois introduz um componente entre as duas camadas (dados e interface) chamada Controller, o Design Patter MVC está mais ligado a arquitetura da aplicação do que um tipo de padrão de projeto, pois afeta a maneira de como a aplicação será organizada e estruturada.

O MVC não abrange a camada de acesso a dados, pois supõe-se que ela esteja encapsulada dentro do model.

OBS: Camadas dizem como separar os componentes, mas MVC diz como os componentes interagem entre si.

As camadas:

  • Model: A representação especifica da informação em que a aplicação trabalha. Por exemplo, cliente, fornecedor e produtos fazem parte do domínio de um sistema de PDV. Não confunda Model com outro nome da camada de dominio. Lógica de dominio apresenta traz maior sentido aos dados crus. Ex: Temos um cliente com um saldo de crédito X, e um histórico de compras e pagamentos, e criamos um método que percorre o histórico de compras do cliente e de pagamentos e nos devolve se podemos ou não aumentar o crédito do cliente, isso é chamada lógida de domínio, onde é agregado maior sentido aos dados propriamente ditos "crus".
  • View: A camada view é responsável por expor os dados a uma iteração com o usuário lendo do Model as informações necessárias.
  • Controller: Controller é onde ocorre todo o processamento, validações, manipulação de eventos e solicitações do usuário.

O diagrama representa as seguintes associações:

  • O controller conhece a view e o model.
  • A view conhece apenas o model.
modelviewcontrollerdiagram

Modelo MVC

Fluxo:

  1. Ocorre uma interação do usuário com a interface ( clique em um botão de listagem).
  2. O controller recebe a solicitação pois havia sido previamente definido em uma rotina a manipulação do evento no proprio controller.
  3. O controller acessa o model baseado na interação do usuário.
  4. A view utiliza o model para gerar a interface( na maioria dos casos exibir os dados ou requisições do usuário),  a view obtem os dados do model sem que ele tenha conhecimento da camada de apresentação.
  5. A view espera as próximas interações do usuário e o ciclo se inicia.

Bem vamos a parte prática:

Crie a seguinte estrutura de pacotes:

pacotes_exemplo_flex

Agora vamos criar os componentes MXML dentro do pacote view:


<?xml version="1.0" encoding="utf-8"?>
<mx:Panel title="Listagem" creationComplete="init()" xmlns:mx="http://www.adobe.com/2006/mxml" width="400" height="300">
<mx:List id="list" width="100%" dataProvider="{model.clients}" labelField="name" height="100%" />
<mx:ApplicationControlBar>
<mx:Button id="btList" label="Listar" />
<mx:Button id="btClear" label="Limpar" />
</mx:ApplicationControlBar>
<mx:Script>
<![CDATA[
import br.com.ronaldorigoni.controller.ListagemController;
import br.com.ronaldorigoni.model.ListagemModel;
[Bindable]
public var model:ListagemModel = null;
public var controller:ListagemController = null;

private function init():void{
model = new ListagemModel();
controller = new ListagemController(this);
}
]]>
</mx:Script>
</mx:Panel>

Sublistagem.mlxml


<?xml version="1.0" encoding="utf-8"?>
<mx:Panel title="Sublistagem" creationComplete="init()" xmlns:mx="http://www.adobe.com/2006/mxml" width="400" height="300">
<mx:Form>
<mx:FormItem label="ID:">
<mx:TextInput editable="false" id="clientId" text="{model.client.id}" />
</mx:FormItem>
<mx:FormItem label="Name">
<mx:TextInput editable="false" id="clientName" text="{model.client.name}"/>
</mx:FormItem>
</mx:Form>
<mx:Script>
<![CDATA[
import br.com.ronaldorigoni.controller.SublistagemController;
import br.com.ronaldorigoni.model.SublistagemModel;
[Bindable]
public var model:SublistagemModel = null;
public var controller:SublistagemController = null;

private function init():void{
model = new SublistagemModel();
controller = new SublistagemController(this);
}
]]>
</mx:Script>
</mx:Panel>

Em seguida criaremos os controllers para cada componente da camada view:

ListagemController.as


package br.com.ronaldorigoni.controller{
import br.com.ronaldorigoni.event.ListagemEvento;
import br.com.ronaldorigoni.view.Listagem;
import br.com.ronaldorigoni.vo.Client;

import flash.events.MouseEvent;

import mx.collections.ArrayCollection;
import mx.events.ListEvent;

public class ListagemController{
// instancia da camada view para o controller poder manipulá-la
private var view:Listagem = null;
/**
* Contrutor do controller
* @param _view A instancia da view
*/
public function ListagemController(_view:Listagem){
// atribue a instancia da view a instancia local
this.view = _view;
// adiciona evento de clique para listar clientes
view.btList.addEventListener(MouseEvent.CLICK, listClients);
// adiciona evento para limpar a listagem de clientes
view.btClear.addEventListener(MouseEvent.CLICK, clearClients);
// adiciona evento de selecao de um cliente
this.view.list.addEventListener(ListEvent.ITEM_CLICK, dispatchEvent);
}
/**
* Invoca preenchimento do modelo de clientes.
* Lembrando que em uma aplicacao real isso deve ser
* feito por uma linguagem de servidor, geralmente com acesso
* a banco de dados.
*/
private function listClients(event:MouseEvent):void{
fillClients();
}

/**
* Limpa a lista de clientes.
*/
private function clearClients(event:MouseEvent):void{
view.model.clearModel();
}

/**
* Preenche a lista de clientes.
* Sendo quem em uma aplicação real estes dados devem ser alimentados
* por uma linguagem de servidor.         *
*/
private function fillClients():void{
for(var i:int = 0; i < 10; i ++){
var client:Client = new Client();
client.name = "Client "+i;
client.id = i;
view.model.addClient(client);
}
}

/**
* Dispara um evento para o componente pai da listagem, e este
* terá adicionado os listeners para este tipo de evento.
* Onde fará a invocação das funcoes.
*/
private function dispatchEvent(event:ListEvent):void{
// setando o cliente selecionado dentro do modelo de listagem
view.model.selected = view.list.selectedItem as Client;
var listagemEvento:ListagemEvento =    new ListagemEvento(ListagemEvento.EVENT_CLIENT_SELECTED);
// inserindo dentro do objeto de evento o cliente selecionado.
listagemEvento.sublistagemModel.client = view.model.selected;
// invoca o componente pai da view no caso a application
// para disparar um evento, sendo que nela esta definido um ouvinte para este
// tipo de evento, e a funcao será invocada, internamente no evento existe os dados
// que a visão necessita para exibir.
view.parent.dispatchEvent(listagemEvento);
}
}
}

SublistagemController.as


package br.com.ronaldorigoni.controller{
import br.com.ronaldorigoni.event.ListagemEvento;
import br.com.ronaldorigoni.view.Sublistagem;

import mx.controls.Alert;
public class SublistagemController{

/**
* Instancia da camada view
*/
private var view:Sublistagem = null;

/**
* Construtor do controller, recebe instancia da camada view para
* poder manipulá-la
*/
public function SublistagemController(_view:Sublistagem){
view = _view;
}
/**
* Manipula o evento de cliente selecionado.
*/
public function handleEventSelected(event:ListagemEvento):void{
// seta o titulo do painel de sublistagem
view.title = "Sublistagem       Evento recebido:"+event.type;
// seta o model da sublistagem
// quando o model é setado ele é automaticamente renderizado
// na view, pois está marcado com o [Bindable]
view.model = event.sublistagemModel;
}
}
}

Agora vamos criar as Classes de Modelo para os dois componentes visuais da camada View:

ListagemModel.as


package br.com.ronaldorigoni.model{
import br.com.ronaldorigoni.vo.Client;

import mx.collections.ArrayCollection;

public class ListagemModel{
/**
* Array de clientes que será exibido na listagem.
*/
private var _clients:ArrayCollection = new ArrayCollection();
private var _selected:Client;

public function ListagemModel(){
}
/**
* Defina o arrayCollection de clientes.
*/
public function set clients(clients:ArrayCollection):void{
this._clients = clients;
}
/**
* Retorna o arrayCollection de clientes
*/
public function get clients():ArrayCollection{
return _clients;
}
/**
* Limpa o array de clientes.
*/
public function clearModel():void{
this._clients.removeAll();
}
public function addClient(client:Client):void{
_clients.addItem(client);
}

public function set selected(selected:Client):void{
this._selected = selected;
}
public function get selected():Client{
return _selected;
}
}
}

E SublistagemModel.as


package br.com.ronaldorigoni.model{
import br.com.ronaldorigoni.vo.Client;

public class SublistagemModel{

/**
* Representa o cliente atualmente selecionado.
*/
private var _client:Client = new Client();

public function SublistagemModel(){
}

/**
* Seta o cliente atualmente selecionado.
*/
public function set client(client:Client):void{
this._client = client;
}
/**
* Recupera o cliente atual selecionado.
*/
public function get client():Client{
return _client;
}
}
}

Agora vamos criar a classe de Eventos que armazenará uma instância de SublistagemModel para notificar o controller de Sublistagem.

</pre>
package br.com.ronaldorigoni.event{
import br.com.ronaldorigoni.model.SublistagemModel;

import flash.events.Event;
/**
* Classe de eventos para operacoes de listagem.
* Armazena o cliente atual selecionado.
*/
public class ListagemEvento extends Event{

/**
* Representa um evendo de cliente selecionado.
*/
public static const EVENT_CLIENT_SELECTED:String = "clientSelected";

private var _sublistagemModel:SublistagemModel = new SublistagemModel();
public function ListagemEvento(tipo:String){
super(tipo,true);
}
/**
* Define o modelo selecionado a sublistagem
*/
public function set sublistagemModel(sublistagemModel:SublistagemModel):void{
this._sublistagemModel = sublistagemModel;
}
/**
* Recupera o modelo da sublistagem
*/
public function get sublistagemModel():SublistagemModel{
return _sublistagemModel;
}
}
}

Esta classe de evento mantem uma propriedade do tipo cliente que alimentará o model de Sublistagem.

Agora vamos criar a classe de VO Client.as, que armazena as informações de cada cliente.


package br.com.ronaldorigoni.vo{
/**
* VO de cliente.
*/
public class Client{

private var _id:uint;
private var _name:String;

public function Client(){
}
/**
* seta o nome do cliente
*/
public function set name(name:String):void{
this._name = name;
}
/**
* Seta o id do cliente;
*/
public function set id(id:uint):void{
this._id = id;
}
/**
* Retorna o atual nome do cliente
*/
public function get name():String{
return _name;
}
/**
* Retorna o id do cliente
*/
public function get id():uint{
return _id;
}
}
}

E por fim nosso código do nosso application ExemploMVC.mxml:


<?xml version="1.0" encoding="utf-8"?>
<mx:Application creationComplete="init()" xmlns:mx="http://www.adobe.com/2006/mxml" layout="horizontal" xmlns:view="br.com.ronaldorigoni.view.*">
<mx:HBox>
<view:Listagem id="listagem"/>
<view:Sublistagem id="sublistagem"/>
</mx:HBox>
<mx:Script>
<![CDATA[
import br.com.ronaldorigoni.event.ListagemEvento;
/**
* Funcao é disparada quando a aplicacao terminar de carregar.
*/
private function init():void{
/**
* Adiciona o listener de evento, para quando for disparado um
* evento deste tipo a funcao para manipular seja invocada.
*/
addEventListener(ListagemEvento.EVENT_CLIENT_SELECTED,
sublistagem.controller.handleEventSelected);
}
]]>
</mx:Script>
</mx:Application>

No application tem um listener adicionado para quando for disparado um evento do tipo "ListagemEvento.EVENT_CLIENT_SELECTED", que dispara a função "SublistagemConroller.handleEventSelected", que captura dentro do evento o cliente selecionado atravéz da propriedade "selected", e atualiza o modelo de sublistagem fazendo com que o cliente selecionado chege até a sublistagem sem que os componentes esteja acoplados.

Para testarmos o funcionamento rode a aplicação como Flex Application, clique sobre "Listar", onde o controller manipula este clique e lista os clientes, quando for clicado sobre um cliente na List da Listagem.mxml será criado uma instância de ListagemEvento e adicionado o Client selecionado dentro deste evento e é disparado através do componente pai de Listagem.xml o evento criado, como temos um listener para este tipo de evento em ExemploMVC.mxml ele dispara a função com este evento como um parâmetro, e la é feita a manipulação e atualização do model de Sublistagem.

A grande vantagem de se programar desta forma é que os componentes não ficam acoplados e não se conhecem em tempo de compilação. Já esta estrutura de MVC permite que possamos futuramente efetuar alterações em cada camada sem que outra camada seja afetada ou alterado o funcionamento. Ex, se precisarmos alterar o layout da aplicação o controller não precisa saber disso.

Com o uso da anotação de metadados "Bindable", nos modelos dentro da view, faz com que qualquer alteração pelo Controller sobre o model ela seja automaticamente exibida na camada view, e no caso inverso, quando o usuário altera algum dado apresentado na camada view, ele já é autimaticamente atualizado no model, sendo assim o controller tem total controle sobre a exibição e alteração dos dados.

Aparentemente com esta estrutura temos mais código, mas isso é relativamente incomparável quando for um sistema relativamente grande e que necessite de constante manutenção, pois será alterado apenas as partes que necessitam sem afetar as demais.

Todas as classes estão comentadas e explicadas, qualquer dúvida crítica ou sugestão me escreva ronaldo arroba ronaldorigoni.com.br

Segue link para download do projeto.

Abraços e até um próximo post.

  • Português Português
  • Italiano Italiano
  • English English