|
Project Information
Featured
Downloads
Links
|
IntroductionBindageTools is an alternative to Flex's BindingUtils class, providing an intuitive, flexible API for creating and managing data bindings. Out of the box, Flex provides great support for declarative data binding in MXML. Declarative bindings are null-safe, run automatically on set up, and are automatically torn down when components are removed from the stage. Additionally, declarative bindings allow you to combine data from several places (e.g. text="Welcome {person.name + ' ' + person.surname}"). However, once you drop down to ActionScript most of that convenience goes away. Flex's built-in BindingUtils class only has a few features, and its API is a little awkward. BindageTools bridges the data binding feature gap between ActionScript and MXML. It also provides many additional features not available in MXML to overcome common UI binding problems (continue reading below for examples). News2012-03-12: Version 0.0.6 released
See ChangeLog for full history. ExamplesSimple one-way bindingBind.fromProperty(model, "submitEnabled")
.toProperty(submitButton, "enabled");
Two-way bindingsBind.twoWay(
Bind.fromProperty(person, "name"),
Bind.fromProperty(nameInput, "text"));
Data validationBind.fromProperty(ageStepper, "value")
.validate(greaterThanOrEqualTo(0)) // (Hamcrest matcher)
.toProperty(person, "age");Data conversionBind.fromProperty(ageInput, "text")
.convert(toNumber())
.toProperty(person, "age");
Custom conversionfunction toTitleCase(value:String):String {
return value.replace(/\b./g, // match first letter of each word
function(match:String, ... rest):String {
return match.toUpperCase(); }
});
}
Bind.fromProperty(document, "title")
.convert(toTitleCase)
.toProperty(titleLabel, "text");
Conditional conversionBind.fromProperty(person, "name")
.convert(ifValue(isA(emptyString()))
.thenConvert(toConstant("This field is required"))
.elseConvert(toConstant(null)))
.toProperty(nameInput, "errorString");
Validate for conversion, convert, then validate converted valueBind.fromProperty(ageInput, "text")
.validate(re(/\d+/)) // ("re" is the Hamcrest matcher for RegExp)
.convert(toNumber())
.validate(greaterThan(0))
.toProperty(person, "age");
Two-way binding with validation and conversionBind.twoWay(
Bind.fromProperty(model, "age")
.convert(valueToString()),
Bind.fromProperty(ageInput, "text")
.validate(isNumeric())
.convert(toNumber())
.validate(greaterThan(0)));
Bind from a property to a handler functionfunction selectAllOrNone(all:Boolean):void {
if (all) {
selectAll();
}
else {
deselectAll();
}
}
Bind.fromProperty(selectAllCheckbox, "selection")
.toFunction(selectAllOrNone);
Interpolate values into StringsBind.fromProperty(user, "name")
.format("Welcome, {0}!")
.toProperty(welcomeLabel, "text");
Log when data travels through a binding pipelineBind.fromProperty(person, "name")
.log(LogEventLevel.DEBUG, "person.name changed to {0}")
.convert(valueToString())
.log(LogEventLevel.DEBUG, "name converted to String {0}")
.toProperty(nameInput, "text");
Bind from multiple sourcesBind.fromAll(
Bind.fromProperty(billingSameAsShippingCheckbox, "selection"),
Bind.fromProperty(shippingAddressInput, "text"),
Bind.fromProperty(billingAddressInput, "text")
)
.convert(function(billSameAsShip:Boolean, shipValue:String, billValue:String):String {
// Converter function is called with argument in same order as the source bindings above
return billSameAsShip ? shipValue : billValue;
})
.toProperty(order, "billingAddress");
Bind some condition to the enablement/visibility of a controlThe login button should be enabled only if both the username and password fields are non-empty. Bind.fromAll(
Bind.fromProperty(userNameInput, "text"),
Bind.fromProperty(passwordInput, "text")
)
.convert(toCondition(args(), everyItem(not(emptyString()))))
.toProperty(loginButton, "enabled");
Group related bindings so they do not step on eachotherSuppose we have a UI for entering a coupon code. We have a checkbox, "Do you have a coupon?" and a text input to enter the code. These two UI elements represent a single field in the model. When there is a coupon code in the model, the checkbox should be selected, and the coupon input should display the coupon code. Grouping helps solve the problem where complementary bindings make a roundtrip and overwrite eachother. If groups were omitted in this example, and if the coupon input field was blank, then selecting the checkbox would trigger binding 3, which would set null to the coupon code in the model (since no coupon code is entered in the text box). Setting the model would in turn trigger binding 1 with the blank value, setting the checkbox selection back to false. When two or more bindings are grouped together, then only one binding in the group may execute at a time. If one binding in a group is running when another is triggered, the second binding simply aborts until the next property change. Note that bindings created using Bind.twoWay are already grouped transparently for you. var couponCodeGroup:BindGroup = new BindGroup();
Bind.fromProperty(order, "couponCode") // binding 1
.group(couponCodeGroup)
.convert(toCondition(not(equalTo(null))))
.toProperty(hasCouponCheckbox, "selection");
Bind.fromProperty(order, "couponCode") // binding 2
.group(couponCodeGroup)
.toProperty(couponCodeInput, "text");
Bind.fromAll( // binding 3
Bind.fromProperty(hasCouponCheckbox, "selection"),
Bind.fromProperty(couponCodeInput, "text")
.convert(emptyStringToNull())
)
.group(couponCodeGroup)
.convert(function(hasCoupon:Boolean, couponCode:String):String {
return hasCoupon ? couponCode : null;
})
.toProperty(order, "couponCode");Set up a binding without running it right awayOccasionally it is necessary to set up a binding but not run it right away--instead, it should run the next time the source property or properties change. function validateName(name:String):String {
return name && name.length > 0
? null
: "Name is required.";
}
// Do not show error message initially
Bind.nextTime()
.fromProperty(model, "name")
.convert(validateName)
.toProperty(nameInput, "errorString");Swiz IntegrationSee SwizIntegration |