Utilização de Custom Events para transportar informação entre classes
A 11 de Março elaborei um post relevante sobre a utilização de eventos aquando da criação de componentes fracamente acoplados. Em resumo, o que se pretendia era eliminar a utilização do “parent” na comunicação entre classes. A solução exemplificada consistia em detectar um clique numa opção de um menu e lançar um evento a “informar” a necessidade de mudança de secção. A classe acima teria que escutar esse evento, e reagir fazendo então a mudança de secção .
Aconselho vivamente a leitura desse post para facilitar a compreensão deste.
Após esse post, uma das principais questões que me fizeram, foi “e como transportar dados no evento?”.
Exemplo
Imaginemos um site com a seguinte estrutura:
- Stage
- Stage.seccaoDeProdutos
- Stage.seccaoDeProdutos.listagem
- Stage.seccaoDeProdutos.listagem.itemProduto1
- Stage.seccaoDeProdutos.listagem.itemProduto2
- Stage.seccaoDeProdutos.listagem.itemProduto[n]
- Stage.seccaoDeProdutos.produtoDetalhe
Pretende-se que a classe Listagem detecte cliques nos produtos. Sempre que há um clique num produto, a classe SeccaoDeProdutos deve ser informada e reagir, removendo a listagem, e mostrando o ProdutoDetalhe com os dados do produto clicado.
Graças ao post anterior, já sabemos como resolver a primeira metade deste problema (reagir ao clique, remover a listagem, e mostrar o ProdutoDetalhe), como se demonstra adiante:
Reagir ao clique
Exemplo do Listagem.as
// Considerando que listaDeProdutos é uma matriz com instâncias da
// classe Produto (id:Number, designacao:String, descricao:String, foto:String)
for (var i:Number=0; listaDeProdutos.length>i; ++i)
{
var p:ItemProduto=new ItemProduto(); // Criar um ItemProduto
p.dados=listaDeProdutos[i]; // Colocar os dados do produto dentro do ItemProduto
addChild (p) // Colocar o ItemProduto no ecrã
p.addEventListener(MouseEvent.CLICK, produtoClickHandler); // Detectar os cliques no itemProduto
}
private function produtoClickHandler(ev:MouseEvent):void
{
dispatchEvent(new Event('produtoClick'));
}
No SeccaoDeProdutos.as:
var l:Listagem=new Listagem();
addChild(l);
l.addEventListener('produtoClick', listagemProdutoClickHandler);
private function listagemProdutoClickHandler(ev:Event):void
{
trace('Agora bastaria remover o listagem, e instanciar o ProdutoDetalhe com os dados do produto clicado');
trace('Mas... qual é o produto clicado????')
}
Transportar dados
Entramos agora na segunda parte do problema: como entregar à classe SeccaoDeProdutos o produto que foi clicado?
A solução consiste em criar um Custom Event: uma classe nossa que estende a classe Event, e pode por isso levar mais propriedades – sendo assim usada para transportar informação de um lado para o outro.
Na solução acima, no produtoClickHandler, fazemos dispatchEvent(new Event(‘produtoClick’)); Se interpretarmos esta linha de código, vemos que está a ser lançado um evento do tipo “Event”, com o tipo “produtoClick”. É aqui que queremos lançar um evento nosso, que vamos ter que criar previamente, como se explica adiante.
Criar um Custom Event
Criar um Custom Event não é mais que criar uma classe que estende a classe Event. Vamos assim criar a nossa classe ProdutoEvent:
ProdutoEvent.as:
package
{
import flash.events.Event;
public class ProdutoEvent extends Event
{
public function ProdutoEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false)
{
super(type, bubbles, cancelable);
}
}
}
Transportar dados no Custom Event
O código acima é o suficiente para definir um novo evento. Porém, ainda não chega para o nosso caso: queremos que o nosso evento possa transportar dados. Assim, a solução passa simplesmente por adicionar uma propriedade pública ao nosso evento:
ProdutoEvent.as:
package
{
import flash.events.Event;
public class ProdutoEvent extends Event
{
public var produto:Produto;
public function ProdutoEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false)
{
super(type, bubbles, cancelable);
}
}
}
Agora que temos o evento criado, já o podemos usar. Alteramos assim o código do Listagem.as :
Listagem.as:
private function produtoClickHandler(event:MouseEvent):void
{
var produtoEvent:ProdutoEvent=new ProdutoEvent('produtoClick');
produtoEvent.produto= (event.currentTarget as ItemProduto).dados ; // Vamos colocar dentro do nosso evento (ev.produto) os dados que vamos buscar ao ItemProduto clicado (ev.currentTarget as ItemProduto), que estão na propriedade dados, como vimos no código inicial
dispatchEvent(produtoEvent);
}
Agora na classe SeccaoDeProdutos.as, temos que apanhar o tipo certo de evento no handler, e já conseguimos aceder aos dados transportados. Basta fazer a seguinte alteração:
SeccaoDeProdutos.as:
private function listagemProdutoClickHandler(ev:ProdutoEvent):void
{
trace('Agora bastaria remover o listagem, e instanciar o ProdutoDetalhe com os dados do produto clicado');
trace('O produto clicado foi: ' + ev.produto.designacao + ' TARAAAAM!');
}
Temos com isto um evento que pode carregar dentro de si dados preenchidos na classe que o lança, para que possam ser usados pela classe que o apanha.
Criação de tipos no Custom Event
O código acima já resolve o problema, mas pode ser melhorado de forma a evitar a string “produtoClick”. O ideal seria conseguirmos obter o mesmo comportamento que temos com a classe MouseEvent: fazermos algo como MouseEvent.CLICK em vez de escrevermos à mão “produtoClick”.
Para isso basta criarmos dentro da classe ProdutoEvent constantes estáticas do tipo String, uma para cada tipo de evento possível. Por exemplo:
ProdutoEvent.as:
package
{
import flash.events.Event;
public class ProdutoEvent extends Event
{
public static const VIEW:String="view";
public static const DELETE:String="delete";
public static const UPDATE:String="update";
// etc... colocar aqui todos os tipos que quisermos aplicar à classe ProdutoEvent
public var produto:Produto;
public function ProdutoEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false)
{
super(type, bubbles, cancelable);
}
}
}
Agora quando quisermos preparar um ProdutoEvent, deixamos de fazer:
var ev:ProdutoEvent=new ProdutoEvent('produtoClick');
e passamos a fazer:
var ev:ProdutoEvent=new ProdutoEvent(ProdutoEvent.VIEW);
Para escutarmos o evento, deixamos de fazer:
l.addEventListener('produtoClick', listagemProdutoClickHandler);
e passamos a fazer:
l.addEventListener(ProdutoEvent.VIEW, listagemProdutoClickHandler);
Criei um exemplo completo que demonstra a aplicação prática da explicação acima. Se a minha explicação não tiver sido suficiente, espero que o exemplo ajude a perceber a criação e utilização de Custom Events.
3 Comments
Make A CommentComments RSS Feed TrackBack URL
This entry was written by
July 14th, 2008 at 1:59 pm
excelente exemplo joão, essa é uma questão que os meus formandos estão constantemente a perguntar, como é que passo dados num evento, aqui ficam com mais uma explicação.
July 15th, 2008 at 9:42 am
João,
Eu pessoalmente prefiro referir o objecto como event.target no lugar de event.currentTarget para evitar possíveis problemas caso o evento faça bubbling. De qualquer forma, excelente post!
October 6th, 2008 at 10:47 am
Bom artigo.
Duas coisas que podem interessar:
1) Em classes que não sejam extend de DisplayObject para usarmos o dispatchEvent podemos fazer o implements IEventDispatcher, outra hipotese é fazer o extends EventDispatcher
2) Fica um link para criar a static EventDispatcher
http://www.gskinner.com/blog/archives/2007/07/building_a_stat_1.html