Bindings are one of the most important aspects of Sproutcore. Bindings give us the ability to do complex UI interactions with very little code. They have turned tedious and error-prone code to update the interface a thing of the past. It now becomes a simple setup and configuration task to wire up things to be automatically updated. They are incredibly powerful, but can be easily confusing and misused.
Bindings are just configuration that one sets up so that when a value changes it changes all the elements that are connected to it. They are found most often in the view layer, but are used heavily in the controller and the model as well. We will focus on the view layer. Here is a simple picture of what bindings do for you:
The general concept is that all the views connect to one value ‘name’ in the controller so that if it changes at any time it is reflected automatically in the views. This is the power of bindings.
the basic rules for bindings
There are a few simples rules to follow when using bindings in sproutcore. If you follow these, you will have a much better understanding of what is occurring and will make code debugging much easier. Often times if your model and controller layers are set up correctly, most of your bugs will ben in your bindings.
Rule #1: Use the ‘-Binding’ naming convention ONLY for a bindings
When you set up a binding you uses the following convention:
// DO THIS nameBinding: 'myApp.modelController.name', // NOT THESE skiBinding: 'Rossignol' glueBinding: 'Very Firm'
Often you might want to pass bindings through a view to another view. DO NOT use the ‘-Binding’ convention! Think of anything with the postfix of ‘-Binding’ as a reserved word. This will set up a real binding that will update whenever something changes it will update. This will slow your app down as things are being updated without the need.
Rule #2: Use the ‘-Path’ naming convention to pass a binding to another view
If you do need to pass a binding to a child view or pass it around for some reason, use the ‘-Path’ naming convention:
A further discussion of using this convention can be found in the discussion about composite views.
Rule #3: View Bindings must only be set up in the ‘.lproj’ layer
Fight the urge to set up bindings for the view layer anywhere but in the ‘.lproj’ layer. You should never be directly setting bindings in a view class even if you know what something is supposed to be. This is a maintenance nightmare and will cause lots of problems if you ever have to change something. It also violates some of the basic OO design structure. If you need a bind set up further down, use the ‘-Path’ naming convention.
Rule #4: Use One Way Bindings whenever possible
Always use a One Way Binding when you can. This will speedup performance because the SC RunLoop won’t have to do extra work to update values and observe values on both ends. Example:
This is particularly useful when you are creating views that are for display purposes only. There is no need to have any changes to the view be observed for change.
Using Bindings in Sproutcore
When you set up bindings you are actually setting up a binding pattern that is created at run time in the init() method. This is why it is very important to add the sc_super() to the top of your init() if you ever overwrite it.
The ‘*’ Trek Convention
Often times you will see a ‘*’ being used in some bindings in sproutcore. This little feature is one of the massive improvements that sproutcore has made to Binding from Cocoa. The ‘*’ creates a chained observation structure that is very powerful and causes less code to have to be created.
The ‘*’ creates the observation point in the binding observation chain. Everything to the left of the ‘*’ is the object that is to be observed and everything to the right of the ‘*’ is the property chain that is being observed for changes. Example:
An english reading of this would be: “Observe ‘myApp.modelController’ for changes to the property ‘otherProperty.name’”. This means that any change to ‘otherProperty’ or ‘otherProperty.name’ will trigger a binding update. This is particularly useful when you want to observe an array for changes to the whole thing or to the size of the array.
You never want to add a ‘*’ to the last property it is already implied:
nameBinding: 'myApp.modelController.name' // Same as... nameBinding: 'myApp.modelController*name'
Absolute and Relative Paths
Most of the time you will be using an absolute path for setting up your bindings. This is the most common form that you will use. Often you might want to make a relative path for a binding. This will be used when you want to bind a child view to a parent view in a view class. You can still use the ‘*’ naming convention. Here is an example of a relative path:
nameBinding: '.parentView.name' descriptionBinding: '.parentView*otherObject.name'
Setting up bindings implicitly
There are a few times where you might want to set up binding explicitly. The best to use the .bind() method on SC.Object:
You will want to use this rarely and only when you want to do some dynamic binding that can change or morph. For the general uses, you want to avoid using this at all costs, but it is good to know for future references.
Bindings are incredibly powerful and are the base of what makes Sproutcore so cool. In a later post we will talk about how to use bindings to your advantage in the controller layer with the Root Controller methodology.