Fléchir:existe-t-il une liaison de données programmatique indolore ?
-
08-06-2019 - |
Question
Je n'ai fait qu'un peu de développement Flex jusqu'à présent, mais j'ai préféré l'approche consistant à créer des contrôles par programme sur des fichiers MXML, car (et s'il te plaît, corrigez-moi si je me trompe !) J'ai compris que vous ne pouvez pas avoir les deux - c'est-à-dire avoir la fonctionnalité de classe dans un fichier de classe ActionScript séparé mais avoir les éléments contenus déclarés dans mxml.
Il ne semble pas y avoir beaucoup de différence en termes de productivité, mais effectuer la liaison de données par programme semble un peu moins que trivial.J'ai jeté un œil à la façon dont le compilateur mxml transforme les expressions de liaison de données.Le résultat est un tas de rappels générés et beaucoup plus de lignes que dans la représentation mxml.Voici donc la question : existe-t-il un moyen d'effectuer une liaison de données par programme qui n'implique pas beaucoup de souffrance ?
La solution
N'ayez pas peur de MXML.C'est idéal pour disposer des vues.Si vous écrivez le vôtre réutilisable Les composants puis les écrire en ActionScript peuvent parfois vous donner un peu plus de contrôle, mais pour les vues non réutilisables, MXML est bien meilleur.C'est plus concis, les fixations sont extrêmement simples à mettre en place, etc.
Cependant, les liaisons en ActionScript pur ne doivent pas nécessairement être très pénibles.Cela ne sera jamais aussi simple que dans MXML où beaucoup de choses sont faites pour vous, mais cela peut être fait sans trop d'effort.
Ce que tu as c'est BindingUtils
et ce sont des méthodes bindSetter
et bindProperty
.J'utilise presque toujours le premier, car je veux généralement travailler ou appeler invalidateProperties
lorsque les valeurs changent, je ne souhaite presque jamais simplement définir une propriété.
Ce que vous devez savoir, c'est que ces deux-là renvoient un objet du type ChangeWatcher
, si vous souhaitez supprimer la liaison pour une raison quelconque, vous devez conserver cet objet.C'est ce qui rend les liaisons manuelles dans ActionScript un peu moins pratiques que celles dans MXML.
Commençons par un exemple simple :
BindingUtils.bindSetter(nameChanged, selectedEmployee, "name");
Ceci configure une liaison qui appellera la méthode nameChanged
quand le name
propriété sur l'objet dans la variable selectedEmployee
changements.Le nameChanged
La méthode recevra la nouvelle valeur du name
propriété comme argument, elle devrait donc ressembler à ceci :
private function nameChanged( newName : String ) : void
Le problème avec cet exemple simple est qu'une fois que vous avez configuré cette liaison, elle se déclenchera à chaque fois que la propriété de l'objet spécifié change.La valeur de la variable selectedEmployee
peut changer, mais la liaison est toujours configurée pour l'objet vers lequel la variable pointait auparavant.
Il existe deux manières de résoudre ce problème :soit pour garder le ChangeWatcher
renvoyé par BindingUtils.bindSetter
autour et appeler unwatch
dessus lorsque vous souhaitez supprimer la liaison (puis configurer une nouvelle liaison à la place), ou vous lier à vous-même.Je vais d'abord vous montrer la première option, puis vous expliquer ce que je veux dire par vous lier à vous-même.
Le currentEmployee
pourrait être transformé en une paire getter/setter et implémenté comme ceci (montrant uniquement le 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");
}
}
}
Ce qui se passe, c'est que lorsque le currentEmployee
La propriété est définie, il regarde s'il y avait une valeur précédente et, si c'est le cas, supprime la liaison pour cet objet (currentEmployeeNameCW.unwatch()
), alors il définit la variable privée, et à moins que la nouvelle valeur ne soit null
établit une nouvelle liaison pour le name
propriété.Plus important encore, cela sauve le ChangeWatcher
renvoyé par l’appel de liaison.
Il s'agit d'un modèle de reliure de base et je pense qu'il fonctionne bien.Il existe cependant une astuce qui peut être utilisée pour rendre les choses un peu plus simples.Vous pouvez plutôt vous lier à vous-même.Au lieu de configurer et de supprimer des liaisons à chaque fois que currentEmployee
modifications de propriété, vous pouvez demander au système de liaison de le faire pour vous.Dans ton creationComplete
gestionnaire (ou constructeur ou au moins un peu plus tôt), vous pouvez configurer une liaison comme ceci :
BindingUtils.bindSetter(currentEmployeeNameChanged, this, ["currentEmployee", "name"]);
Cela établit une liaison non seulement avec le currentEmployee
propriété sur this
, mais aussi au name
propriété sur cet objet.Donc, à chaque fois, change la méthode currentEmployeeNameChanged
sera appelé.Il n'est pas nécessaire de sauvegarder le ChangeWatcher
car la liaison ne devra jamais être supprimée.
La deuxième solution fonctionne dans de nombreux cas, mais j'ai trouvé que la première est parfois nécessaire, notamment lorsque l'on travaille avec des liaisons dans des classes non-vues (puisque this
doit être un répartiteur d'événements et le currentEmployee
doit être lié pour que cela fonctionne).
Autres conseils
Il existe à ce jour.:)
Je viens de publier mon projet de liaison de données ActionScript en open source : http://code.google.com/p/bindage-tools
BindageTools est une alternative à BindingUtils (voir le jeu de mots ici ?) qui utilise une API fluide dans laquelle vous déclarez vos liaisons de données dans un style pipeline :
Bind.fromProperty(person, "firstName")
.toProperty(firstNameInput, "text");
Liaisons bidirectionnelles :
Bind.twoWay(
Bind.fromProperty(person, "firstName"),
Bind.fromProperty(firstNameInput, "text"));
Conversion et validation explicites des données :
Bind.twoWay(
Bind.fromProperty(person, "age")
.convert(valueToString()),
Bind.fromProperty(ageInput, "text")
.validate(isNumeric()) // (Hamcrest-as3 matcher)
.convert(toNumber()));
Etc.Il y a beaucoup plus d'exemples sur le site.Il existe également de nombreuses autres fonctionnalités. Venez y jeter un œil.--Matthieu
Modifier:API mises à jour
Une façon de séparer le MXML et l'ActionScript d'un composant dans des fichiers distincts consiste à faire quelque chose de similaire au modèle de code derrière ASP.Net 1.x.Dans ce modèle, la partie déclarative (le MXML dans ce cas) est une sous-classe de la partie impérative (l'ActionScript).Je pourrais donc déclarer le code derrière pour une classe comme ceci :
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.";
}
}
}
...et le balisage comme ceci :
<?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>
Comme vous pouvez le voir sur cet exemple, un inconvénient de cette approche est que vous devez déclarer des contrôles comme monÉtiquette dans les deux fichiers.
il existe un moyen que j'utilise habituellement pour utiliser mxml et action script ensemble :Tous mes composants mxml héritent d'une classe de script d'action où j'ajoute le code le plus complexe.Ensuite, vous pouvez faire référence aux écouteurs d'événements implémentés dans cette classe dans le fichier mxml.
Salutations,
Ruth