Flexível:existe vinculação de dados programática indolor?
-
08-06-2019 - |
Pergunta
Eu só desenvolvi um pouco o Flex até agora, mas preferi a abordagem de criação de controles programaticamente em vez de arquivos mxml, porque (e por favor, corrija-me se eu estiver errado!) Concluí que você não pode ter as duas coisas - ou seja, ter a funcionalidade da classe em um arquivo de classe ActionScript separado, mas ter os elementos contidos declarados em mxml.
Não parece haver muita diferença em termos de produtividade, mas vincular dados programaticamente parece um pouco menos que trivial.Dei uma olhada em como o compilador mxml transforma as expressões de vinculação de dados.O resultado é um monte de retornos de chamada gerados e muito mais linhas do que na representação mxml.Então aqui está a pergunta: existe uma maneira de vincular dados programaticamente que não envolva muitos danos?
Solução
Não tenha medo do MXML.É ótimo para definir visualizações.Se você escrever o seu próprio reutilizável componentes e, em seguida, escrevê-los em ActionScript pode, às vezes, fornecer um pouco mais de controle, mas para visualizações não reutilizáveis, o MXML é muito melhor.É mais conciso, as ligações são extremamente fáceis de configurar, etc.
No entanto, vinculações em ActionScript puro não precisam ser tão complicadas.Nunca será tão simples como no MXML, onde muitas coisas são feitas para você, mas pode ser feito sem muito esforço.
O que você tem é BindingUtils
e são métodos bindSetter
e bindProperty
.Quase sempre uso o primeiro, pois geralmente quero fazer algum trabalho ou ligar invalidateProperties
quando os valores mudam, quase nunca quero apenas definir uma propriedade.
O que você precisa saber é que esses dois retornam um objeto do tipo ChangeWatcher
, se quiser remover a ligação por algum motivo, você deverá manter esse objeto.É isso que torna as vinculações manuais no ActionScript um pouco menos convenientes do que as do MXML.
Vamos começar com um exemplo simples:
BindingUtils.bindSetter(nameChanged, selectedEmployee, "name");
Isso configura uma ligação que chamará o método nameChanged
quando o name
propriedade no objeto na variável selectedEmployee
mudanças.O nameChanged
método receberá o novo valor do name
propriedade como argumento, então deve ficar assim:
private function nameChanged( newName : String ) : void
O problema com este exemplo simples é que depois de configurar esta ligação, ela será acionada sempre que a propriedade do objeto especificado for alterada.O valor da variável selectedEmployee
pode mudar, mas a ligação ainda está configurada para o objeto para o qual a variável apontou antes.
Existem duas maneiras de resolver isso:seja para manter o ChangeWatcher
retornado por BindingUtils.bindSetter
por aí e ligue unwatch
nele quando quiser remover a ligação (e, em seguida, configurar uma nova ligação) ou vincular a si mesmo.Mostrarei primeiro a primeira opção e depois explicarei o que quero dizer com vinculação a si mesmo.
O currentEmployee
poderia ser transformado em um par getter/setter e implementado assim (mostrando apenas o setter):
public function set currentEmployee( employee : Employee ) : void {
if ( _currentEmployee != employee ) {
if ( _currentEmployee != null ) {
currentEmployeeNameCW.unwatch();
}
_currentEmployee = employee;
if ( _currentEmployee != null ) {
currentEmployeeNameCW = BindingUtils.bindSetter(currentEmployeeNameChanged, _currentEmployee, "name");
}
}
}
O que acontece é que quando o currentEmployee
propriedade está definida, ele verifica se havia um valor anterior e, em caso afirmativo, remove a ligação desse objeto (currentEmployeeNameCW.unwatch()
), então ele define a variável privada e, a menos que o novo valor tenha sido null
estabelece uma nova vinculação para o name
propriedade.O mais importante é que ele salva o ChangeWatcher
retornado pela chamada de ligação.
Este é um padrão de ligação básico e acho que funciona bem.Existe, no entanto, um truque que pode ser usado para tornar isso um pouco mais simples.Você pode se vincular a si mesmo.Em vez de configurar e remover vinculações cada vez que o currentEmployee
alterações de propriedade, você pode fazer com que o sistema de ligação faça isso por você.Na tua creationComplete
manipulador (ou construtor ou pelo menos algum tempo antes), você pode configurar uma ligação assim:
BindingUtils.bindSetter(currentEmployeeNameChanged, this, ["currentEmployee", "name"]);
Isto estabelece uma ligação não só com o currentEmployee
propriedade em this
, mas também para o name
propriedade neste objeto.Então, a qualquer momento, qualquer um deles muda o método currentEmployeeNameChanged
será chamado.Não há necessidade de salvar o ChangeWatcher
porque a ligação nunca terá que ser removida.
A segunda solução funciona em muitos casos, mas descobri que a primeira às vezes é necessária, especialmente quando se trabalha com ligações em classes que não são de visualização (já que this
tem que ser um despachante de eventos e o currentEmployee
tem que ser vinculável para funcionar).
Outras dicas
Existe até hoje.:)
Acabei de lançar meu projeto de vinculação de dados ActionScript como código aberto: http://code.google.com/p/bindage-tools
BindageTools é uma alternativa ao BindingUtils (viu o jogo de palavras aí?) Que usa uma API fluente onde você declara suas ligações de dados em um estilo de pipeline:
Bind.fromProperty(person, "firstName")
.toProperty(firstNameInput, "text");
Ligações bidirecionais:
Bind.twoWay(
Bind.fromProperty(person, "firstName"),
Bind.fromProperty(firstNameInput, "text"));
Conversão e validação explícita de dados:
Bind.twoWay(
Bind.fromProperty(person, "age")
.convert(valueToString()),
Bind.fromProperty(ageInput, "text")
.validate(isNumeric()) // (Hamcrest-as3 matcher)
.convert(toNumber()));
Etc.Existem muitos mais exemplos no site.Há muitos outros recursos também - venha dar uma olhada.--Mateus
Editar:APIs atualizadas
Uma maneira de separar o MXML e o ActionScript de um componente em arquivos separados é fazer algo semelhante ao modelo code-behind do ASP.Net 1.x.Neste modelo a parte declarativa (neste caso o MXML) é uma subclasse da parte imperativa (o ActionScript).Então, eu poderia declarar o código por trás de uma classe como esta:
package CustomComponents
{
import mx.containers.*;
import mx.controls.*;
import flash.events.Event;
public class MyCanvasCode extends Canvas
{
public var myLabel : Label;
protected function onInitialize(event : Event):void
{
MyLabel.text = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit.";
}
}
}
... e a marcação assim:
<?xml version="1.0" encoding="utf-8"?>
<MyCanvasCode xmlns="CustomComponents.*"
xmlns:mx="http://www.adobe.com/2006/mxml"
initialize="onInitialize(event)">
<mx:Label id="myLabel"/>
</MyCanvasCode>
Como você pode ver neste exemplo, uma desvantagem dessa abordagem é que você precisa declarar controles como minha etiqueta em ambos os arquivos.
existe uma maneira que costumo usar para usar mxml e script de ação juntos:Todos os meus componentes mxml herdam de uma classe de script de ação onde adiciono o código mais complexo.Em seguida, você pode consultar os ouvintes de eventos implementados nesta classe no arquivo mxml.
Cumprimentos,
Rute