I’m currently working on an Ember.js web application that is a basic CRUD app with a twist — we don’t know all of the form fields or model attributes ahead of time, but rely on a JSON document to specify the names and types of attributes for which we need to render form controls. This presents some interesting challenges, the most significant being that our input elements don’t know what to bind their value to until they are instantiated.
After trying a few different approaches, we settled on using a pair of observers to accomplish what a more direct binding could not in this scenario. One observer watches the input’s
value property and updates the remote property value, and the other observer watches the remote property and updates the input’s
Read on for a few more details. This example uses Ember.js 1.0.0-RC.1, and we are using the same general solution in 1.0.0-RC.3.
Say we have the following definition for a set of fields:
We want to display a text field for each element in the array and bind its value to a property on our model defined by the
key. This example uses a
CollectionView to render a subclass of
Ember.TextField for each element. The
DynamicBoundTextField here has the pair of observers.
Our implementation uses an intermediate object to perform the observation because it requires specialized knowledge about the context that the binding is being performed in, and we felt it was odd having that information on the input itself. But it works the same way.
Try it out in this JS Bin: Runtime binding using observers JS Bin.
Things that Didn’t Work
TextField with a valueBinding
The primary constraint we’re dealing with is not knowing what the TextField should bind to until it’s created. This means we can’t simply place a
valueBinding on our text input in our class definition like most Ember examples show. It needs another level of indirection to work.
TextField and an Intermediate Object with Bindings
So we tried creating a
valueBinding to a known property,
inputValue, of an intermediate view. The intermediate view then bound its
inputValue to the target property in
init. This caused interesting but unwanted behavior, like the target property getting set to undefined instead of the input’s value getting set to the existing value of the target property. It just didn’t work out very well.
Our current, working implementation does have an intermediate object, but it uses the observer pair pattern instead of a binding to the target property to avoid the bad behavior described above.