2008-08-18 12 views
13

Hasta ahora he hecho un poco de desarrollo de Flex, pero he preferido el enfoque de crear controles mediante programación sobre archivos mxml, porque (y por favor, corrígeme si estoy equivocado). He reunido que no se puede tener en ambos sentidos, es decir, que tiene la funcionalidad de clase en un archivo de clase de ActionScript separado, pero que los elementos contenidos se declaran en mxml.Flex: ¿existe un enlace de datos programático indoloro?

No parece haber mucha diferencia en términos de productividad, pero hacer el enlace de datos programáticamente parece algo menos que trivial. Eché un vistazo a cómo el compilador mxml transforma las expresiones de enlace de datos. El resultado es un grupo de devoluciones de llamada generadas y muchas más líneas que en la representación de mxml. Así que aquí está la pregunta: ¿hay alguna manera de hacer enlaces de datos programáticamente que no implique un mundo de daño?

Respuesta

29

No tenga miedo de MXML. Es genial para diseñar vistas. Si escribe sus propios componentes reutilizables, escribirlos en ActionScript a veces puede darle un poco más de control, pero para las vistas no reutilizables MXML es mucho mejor. Es más escueto, los enlaces son extremadamente fáciles de configurar, etc.

Sin embargo, los enlaces en ActionScript puro no tienen por qué ser tan molestos. Nunca será tan simple como en MXML donde se hacen muchas cosas por ti, pero se puede hacer sin demasiado esfuerzo.

Lo que tiene es BindingUtils y sus métodos bindSetter y bindProperty. Casi siempre uso el primero, ya que generalmente quiero trabajar, o llamo al invalidateProperties cuando cambian los valores, casi nunca me gusta establecer una propiedad.

Lo que necesita saber es que estos dos devuelven un objeto del tipo ChangeWatcher, si desea eliminar el enlace por alguna razón, debe aferrarse a este objeto. Esto es lo que hace que los enlaces manuales en ActionScript sean un poco menos convenientes que los de MXML.

Vamos a empezar con un ejemplo sencillo:

BindingUtils.bindSetter(nameChanged, selectedEmployee, "name"); 

Esto establece una unión que se llamará al método nameChanged cuando la propiedad name en el objeto en la variable selectedEmployee cambios. El método nameChanged recibirá el nuevo valor de la propiedad name como argumento, por lo que debe tener este aspecto:

private function nameChanged(newName : String) : void 

El problema con este ejemplo simple es que una vez que haya configurado esta unión se disparará cada vez la propiedad del objeto especificado cambia. El valor de la variable selectedEmployee puede cambiar, pero el enlace aún está configurado para el objeto al que apuntaba la variable.

Hay dos maneras de resolver este: ya sea para mantener el ChangeWatcher devuelto por BindingUtils.bindSetter alrededor y llame unwatch en él cuando se quiere eliminar el enlace (y luego la creación de una nueva unión en su lugar), o se unen a ti mismo. Primero te mostraré la primera opción, y luego explicaré lo que quiero decir uniéndote a ti mismo.

El currentEmployee se podría hacer en un par de captador/definidor e implementado como esto (sólo muestra el colocador):

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

Lo que sucede es que cuando la propiedad currentEmployee se establece que mira para ver si había un valor anterior, y si es así, quita el enlace para ese objeto (currentEmployeeNameCW.unwatch()), establece la variable privada y, a menos que el nuevo valor sea null, establece un nuevo enlace para la propiedad name. Lo más importante es que guarda el ChangeWatcher devuelto por la llamada vinculante.

Este es un patrón de encuadernación básico y creo que funciona bien. Sin embargo, hay un truco que se puede usar para hacerlo un poco más simple. Usted puede unirse a usted mismo en su lugar. En lugar de configurar y eliminar enlaces cada vez que cambia la propiedad currentEmployee, puede hacer que el sistema de enlace lo haga por usted. En su creationComplete manejador (o constructor o por lo menos algún tiempo antes de tiempo) se puede establecer una unión de este modo:

BindingUtils.bindSetter(currentEmployeeNameChanged, this, ["currentEmployee", "name"]); 

Esto establece una unión no sólo a la propiedad currentEmployee en this, sino también a la propiedad name en este objeto. Por lo tanto, cada vez que se cambie el método se llamará al currentEmployeeNameChanged. No es necesario guardar el ChangeWatcher porque nunca será necesario eliminar el enlace.

La segunda solución funciona en muchos casos, pero he encontrado que la primera es a veces necesario, especialmente cuando se trabaja con los atascamientos en las clases no vista (ya que this tiene que ser un despachador de eventos y la currentEmployee tiene que ser enlazable para que funcione).

+0

Excelente escritura. Esto fue muy útil. ¡Gracias! – airportyh

+0

Excelente redacción de hecho. – Kevin

+0

Escritura impresionante de hecho! Una pregunta rápida. ¿No sería un enfoque simple para la primera solución simplemente verificar si currentEmployeeNameCW es nulo? Si no es nulo, entonces existe una vinculación, así que llame a currentEmployeeNameCW.unwatch(). Parece una solución más generalizada y aún muy sucinta. – rinogo

2

Una forma de separar MXML y ActionScript para un componente en archivos separados es haciendo algo similar al código ASP.Net 1.x detrás del modelo. En este modelo, la parte declarativa (el MXML en este caso) es una subclase de la parte imperativa (el ActionScript). Por lo que podría declarar el código detrás de una clase 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."; 
     } 
    } 
} 

... y el marcado de la siguiente manera:

<?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 se puede ver en este ejemplo, un disadvatage de este enfoque es que debe declarar controles como myLabel en ambos archivos.

+0

La buena razón para hacerlo de esa manera es que no rompe la vista de diseño flexible. Si no te importa mucho la vista de diseño (como yo), entonces hacer la herencia a la inversa es mucho más fácil de manejar. No es necesario declarar previamente (y mantener la sincronización) los niños mxml en su clase .as. También se siente más "correcto". Tome un visual y agregue funcionalidad a él. –

+0

No haga esto ... doblará la cantidad de clases que necesita para una Vista. – Clintm

0

hay una manera en que suelo usar mxml y action script juntos: todos mis componentes mxml heredan de una clase de script de acción donde agrego el código más complejo. Luego puede referirse a los detectores de eventos implementados en esta clase en el archivo mxml.

Saludos,

Ruth

8

Existe a partir de hoy. :)

Me acaba de lanzar mi proyecto de enlace de datos de ActionScript como código abierto: http://code.google.com/p/bindage-tools

BindageTools es una alternativa a BindingUtils (ver el juego de palabras allí?) Que utiliza una API fluida en la que declara sus enlaces de datos en un estilo de tuberías:

Bind.fromProperty(person, "firstName") 
    .toProperty(firstNameInput, "text"); 

fijaciones de dos vías:

Bind.twoWay(
    Bind.fromProperty(person, "firstName"), 
    Bind.fromProperty(firstNameInput, "text")); 

explícita la conversión de datos y validación:

Bind.twoWay(
    Bind.fromProperty(person, "age") 
     .convert(valueToString()), 
    Bind.fromProperty(ageInput, "text") 
     .validate(isNumeric()) // (Hamcrest-as3 matcher) 
     .convert(toNumber())); 

Etc . Hay muchos más ejemplos en el sitio. También hay muchas otras características, ven a echar un vistazo. --Matthew

Editar: APIs actualizadas

+0

es tan genial encontrar a alguien que lo haya hecho, en modo as3 puro muchas gracias ~ – davyzhang

+0

Me complace, me alegra que lo encuentre útil. – qualidafial

Cuestiones relacionadas