SproutCore 1.0: SC.Event

SproutCore has always done its own event handling for the most part.  In fact, it was one of the first framework to implement a technique called “event delegation” to make event handling significantly faster.  In SproutCore 1.0 we’ve improved this event handling with the new SC.Event class.

About Event Delegation

If you’ve ever written regular DHTML-style JavaScript, you know the “standard” way to observe an event is to add an event listener to the DOM element you are interested in.  For example, if you want to receive mousedown events from a BUTTON element, you add an event handler to that element.  In Prototype you use a method like this:

Event.observe('#my-button', 'mousedown', function() {
  ... called on event...
});

This approach works fine when you want to enhance a few elements on your page, but when you try to display thousands of contacts or hundreds of photos or emails, it falls apart.  Each time you register an event handler on an element, there is a cost.  You have to wait for the browser to setup your handler, which can take a lot longer than you might think; especially in IE.

We solved this problem in SproutCore sometime ago using a technique called event delegation.  With this approach, SproutCore registers about 6 event listeners on the root document object when your application loads.   These listeners are notified anytime any event occurs on the page.  From there, SproutCore itself determines which SC.View’s you have created that are interested in receiving these events, if any, and invokes the event handler on that view directly.

How do you indicate interest in receiving an event?  It’s simple: just implement the event handler method.  For example, let’s say you want to receive the mouseDown event from the same button.  Just add the following to your button view class:

mouseDown: function(evt) {
  .. called on event ...
}

And you’re done.  No setup time.  It’s very fast.

Going Further

While event delegation helped speed up event handling in SproutCore, working with events was still not a panacea.  The problem is that the events you receive from browsers differs slightly from platform to platform.  While SproutCore always normalized this to some degree, it still meant you had to implement platform specific code in your handlers quite a bit.

For SproutCore 1.0, we had to implement some additional event handling code anyway to become DOM-library agnostic, so we decided to improve on this aspect as well by introducing a new class called SC.Event.

SC.Event is a W3C-compliant implementation of the Event interface with some useful additions.  All incoming events from the web browser are now wrapped in an SC.Event object before being passed onto your event handler.  This means you can now use the standard SC.Event API to work with any incoming event, regardless of platform.  This alone will make your code simpler.

In addition, SC.Event makes it much easier to dispatch your own custom events or to dispatch mock events for testing in a cross-platform way.  It also simplifies cleanup of event handlers on page unload, which will help to avoid memory leaks in your application.

Using SC.Event

Usually SC.Event will work transparently in the background to make your coding easier.  Whenever you implement an event handler (like mouseDown() above), the event object you are passed will be an instance of SC.Event.  Simply write your code as if you were working in events in Firefox, and you should be good to go.  You can also use some useful additional utility methods defined on the SC.Event class itself such as SC.Event.stop(), which both cancel’s bubbling and prevents the browser’s default action.

If you need to observe an event on a DOM element that is not handled by event delegation, such as the onscroll event, for example, you can also use the SC.Event API to register your own event handler.  Just setup your listener like so:

SC.Event.add(SC.$('#my-button'), 'scroll',  myObject, myObject.buttonOnScroll);

Note that the add() method takes both a target object and a target object method.  This means you can avoid having to call the Function.bind() method that is often required for both jQuery and Prototype code.

If you no longer need to listen to an event, you can remove your observer in a similar way:

SC.Event.remove(SC.$('#my-button'), 'scroll', myObject, myObject.buttonOnScroll);

When your page unloads, all event handlers are automatically removed, so there is no need to do this yourself.

Using SC.Event with Other Libraries

Most DOM-manipulation library provide an API for listening for events.  You can use these alternative methods to listen for events yourself if you want and SproutCore will work just fine.  If you are integrating a component from YUI, jQuery-UI or any other widget library, there is no need to rewrite their event handling.

However, I strongly recommend that you consider using SC.Event and the Responder API for view’s when writing your own code instead.  Since SC.Event is designed specifically for use with SproutCore-style applications and hence it will generally be faster and more efficient for your code.

Getting The Code

SC.Event is part of the new SproutCore 1.0 code base.  You can check it out today on the new Bitburger branch in Git.  See this wiki page for complete instructions on how to try out the code for yourself.

SproutCore 1.0: Introducing CoreQuery

One of the main things we use Prototype for is DOM manipulation.  Whenever a view’s state changes, Prototype help’s SproutCore change CSS classes, edit styles and change element nodes.

Now that we are trying to make SproutCore DOM-library independent, we needed a way for SproutCore’s built-in views to continue to work with the DOM without Prototype installed.  The solution we came up with was to create a new component in SproutCore called CoreQuery.

CoreQuery is a strict subset of jQuery.  In fact, if SproutCore detects jQuery’s presence when it loads, it will use jQuery instead.  By itself, however, CoreQuery is designed to be very small and lightweight.  It’s only about 200 lines of code without comments or whitespace.  It contains only the bare essentials needed by SproutCore view’s to render DOM.

This means that if you are creating a SproutCore application with no custom views, you could opt to use SproutCore without any other DOM library at all.  Or, you could load jQuery, YUI, Prototype, or whatever other library you prefer and use them to help you render your own custom View classes.  You could even choose to use CoreQuery yourself with your custom views.  SproutCore will work the same in all circumstances.

Using CoreQuery

If you want to try CoreQuery yourself, you can find the CoreQuery object in SproutCore 1.0 (aka “Bitburger”) today at SC.$().  This works just like the $() function in jQuery. (Subject to the limitations described below.)

Also, although this is not in the source yet, each SC.View instance will also have a $() that will return a CoreQuery object matching elements inside the view itself.  This will often be the fastest way to work with CoreQuery.  For example,  if you were creating a custom button and needed to update the button state when it changed, you might write:

// invoked whenever the button state changes...
render: function() {
  // set title, enabled state, and selected state...
  this.$().text(this.get('title'))
    .setClass('disabled', !this.get('isEnabled'))
    .setClass('sel', this.get('isSelected'));
},

CoreQuery code, like jQuery, tends to be very clean and concise, which is just what you need for a DOM-manipulation library.  It is a good companion to the more state-oriented approach of bindings and observers.  I’ll have more to say about how you can combine these kinds of API’s in the future, but suffice to say the code is just beautiful and really fun to write.

CoreQuery Limitations…

So what makes CoreQuery different from jQuery?  There are a number of big things left out:

  1. Complex Selectors.  CoreQuery understands simple selectors like “tagname.classname”.  None of the pseudo-selectors provided by jQuery will work.  Also, you can’t do any of the direct-desendant stuff like “parentag > .child-class”.  This is usually not a problem since you often use CoreQuery within the context of a single view and you only need to look for elements with a given class name.
  2. Event Handling. CoreQuery doesn’t know anything about listening for events on DOM elements since this is handled by SproutCore’s built-in event delegation system already. (Which is much more efficient.)
  3. All jQuery plugins. Anything not part of jQuery-Core is omitted.  If you want the full jQuery with plugins, use jQuery.
  4. Effects. jQuery’s effects (i.e. animation) library is not as useful for SproutCore-style views.  Additionally, we are working on something that makes better use of CSS-transitions.
  5. Ajax. SproutCore has its own Ajax functions; no need to copy jQuery.
  6. Script execution.  When you insert HTML that contains a <script> tag in jQuery, the JavaScript inside of those tags will be eval’d.  This does not fit with typical SproutCore usage and its evil anyway, so CoreQuery does not support it.

CoreQuery’s remaining scope is fairly limited to very basic DOM manipulation.  The good news is you generally do not need much else when building SproutCore views.  If you do need more, you can always import your favorite DOM library to get it.  The code overlap will be very minimal.

…And Benefits

In addition to the basic jQuery functions, CoreQuery will also include some additional plugins designed specifically for working with SproutCore-style views.  One such plugin already available is $().setClass(), which will add or remove CSS class names depending on a flag parameter that you pass.  $().setClass() is more efficient than $().addClass()/$().removeClass() for common View operations.

In addition to some utilities methods like this one, we’re also working on something to make animation easier.  To work well on the iPhone, it is important that any animation library SproutCore supports make use of CSS transitions when available.  We’re looking into solutions for this, but the end result will likely be implemented as a CoreQuery plugin.

All enhancement like these are implemented as jQuery-compatible plugins and automatically installed into jQuery if you choose to use it.

Why CoreQuery?

So why create another DOM library?  Why not just create some kind of emulation layer that can hook to Prototype, jQuery etc?

The simple fact is that the functionality provided by most of these libraries is really not that complex when you get down to it; at least not the parts needed by SproutCore.  We found that simply copying the core pieces of a small, properly namespaced library like jQuery ultimately yielded the best performance and had the lowest code overhead than any other option.

Additionally, I liked the simplicity of making SproutCore standalone.  We’re making it much easier to mix and match our framework with others.   I expect to see a lot more hybrid applications in the future that use SproutCore to manage their business logic and drop in UI widgets from jQuery-UI, Scriptaculous, YUI, Cappuccino or various other toolkits when needed.  No extra code is needed to make this work.  Just mashup and go.

A Bright Future

Though CoreQuery is a “hidden technology” used inside of SproutCore, I’m very excited about what it represents for us.  It’s a good example of how we constantly rethink SproutCore’s insides to make sure we’re always delivering a high quality development experience in the web browser.

UPDATE: What about Sizzle? A few people have asked about Sizzle, the cross-library DOM selector library being written by jeresig.  Sizzle is a great idea, but more relevant to lower-level DOM manipulation libraries like Dojo, Prototype or jQuery (the ones who will adopt it).  If you import these libraries and they use Sizzle, then you can use Sizzle with SproutCore.

For SproutCore itself, Sizzle has too much of one thing and not enough of the other.   It’s DOM selector-API is far richer than what we need, which adds unnecessary weight to SproutCore.  On the other hand, it currently lacks any of the other DOM-manipulation tools found in jQuery, which we would have to rewrite from scratch anyway.

As I understand it, Sizzle is expected to gain some of these added features in the future.  If that happens, it is possible CoreQuery will one day be based on Sizzle source instead of jQuery.  Until then, a reduced form of jQuery seems to give us the features we need for the least amount of code, which was the objective.

Try out CoreQuery in SproutCore 1.0 today. Checkout the Bitburger branch now!

At Last: SproutCore 1.0 Code You Can Play With

The legal hold I mentioned a few days ago just cleared today so we are able to start publishing the SproutCore 1.0 code again.  I have just published our current SproutCore 1.0 code base on the “bitburger” branch at Github:

Check out the source: Framework - Build Tools - Samples

You can also see some detailed notes on what we are working on, what is done, what is left to be done, along with instructions on how to try out the code yourself and even how to make contributions.  Just visit our new Bitburger wiki page!:

About Bitburger

Finally, in case you missed them, I’ve been writing about some of the features coming in SproutCore

Where is the SproutCore 1.0 Code?

Although I explained some of this before, a lot of people have noticed there have been no new commits to the Git repo in the last 6 weeks or so.  Let me explain what’s happening.

Portions of SproutCore are being developed by some big companies.  Although those companies are happy to contribute, their contributions needs to go through a legal review process before they can be made available to the public repository.  Due to the major changes in SproutCore 1.0 and the role these changes play, this means we’ve had to temporarily continue SproutCore development in a private repository until the changes are approved by legal and can be submitted back.

Once those changes are made available to the public, you’ll see it in the github repository and I’ll post about it here.  I will also merge in all those wonderful patches that are pending in Lighthouse right now.

Just to be clear, this block is temporary.  Once all of this code hits the public repository, we should be able to resume more public development, at least for parties external to these big companies.

Until then, I’ll continue to post articles here describing some of these features, to be followed up with a post when the code hits pointing you to the source so you can play with it.  Since there are so many great new changes coming, it will be nice to have a collection of articles highlighting the major points the day the code hits.

SproutCore 1.0: UserDefaults

Here’s a fun new bit of code I added to SproutCore for 1.0 over the holiday. The SC.UserDefaults objects that comes with SproutCore 1.0 can be used to store user preferences for your application. It’s terribly easy to use.

First, you will want to set your application and user domains at the top of your main() function like so:

function main() {
  SC.userDefaults.set('appDomain', 'MyCompany:MyApp');  // unique for your application
  SC.userDefaults.set('userDomain', 'charles'); // set to user name
  // ... setup the rest of your app here
};

Next, you will want to setup some reasonable defaults.  The defaults are used if you have not overridden them.  I usually put this into a file on their own called defaults.js, but you could also load these defaults dynamically from a server and fill them in:

SC.userDefaults.defaults("MyCompany:MyApp", {
  "mainTab:nowShowing": "welcome",
  "calendarPane:isVisible": YES
});

Finally, you can get/set attributes like normal:

// determine which pane to show on app load
  SC.page.mainTabs.set('nowShowing', SC.userDefaults.get('mainTab:nowShowing'));

// ..elsewhere, when the tab changes, save the updated view
  nowShowingTabDidChange: function(tabView) {
    SC.userDefaults.set('mainTab:nowShowing', tabView.get('nowShowing'));
    //... other observer code
  }.observes('nowShowing')

All well and good, but so far this looks like syntactic sugar.  Well, here’s the kicker:  if you are using a browser that supports localStorage, including Safari or Firefox, or you have Google Gears installed, userDefaults will automatically save your user preferences locally.  These will override the defaults you write in using the SC.UserDefaults.defaults() method above.  If you are on a browser that does not support client-side storage, things will still work; the preferences will simply reset when you reload.

This new API makes it terribly easy to help your application use client-side storage to remember its last state when you reload the app.  In fact, it’s so easy to use I’ve gone ahead and added support for user defaults to some of the built in views as well.  The SC.TabView, for example, now supports a property called userDefaultKey.  Set this property to some key name, and the tab view will automatically save its last showing tab in user defaults.

SproutCore 1.0: Mixins

There are a lot of little refinements going into SproutCore 1.0.  One of those refinements is better support for Mixins.  A mixin is simply a collection of properties that can be applied to your object or class.  It’s a more flexible way of adding functionality than through class inheritance.   Mixins are supported in SproutCore today, but they get some additional love in SproutCore 1.0 through the addition of initMixin() and destroyMixin().  I’ve added some basic documentation here:

SproutCore Wiki: About Mixins

(Bonus link: draft docs on creating objects and classes)

SproutCore 1.0: Platforms

Mobile is a really interesting area for any web developer.  With the iPhone and Android, there are finally phones with serious browsers that can run actual app-like code.  Very exciting.

Of course, developing an application for a phone has its own unique limitations.  Bandwidth is an issue of course.  As is memory constraints and the UI has to look completely different.

Nevertheless, I believe that the thick-client model that SproutCore is based upon holds great promise for the phone.  Precisely because of the bandwidth constrained, memory limited environment, downloading and installing code that runs right in the web browser can produce a dramatically better experience than forcing the user to wait on regular HTTP or even Ajax requests to complete.  All you need is the framework.

SproutCore was originally designed for desktop applications, where you can potentially display large amounts of data.  Neither the UI controls nor some of the large-data technologies we use on the desktop make sense on the phone.  This is why we are working on creating a mobile version of the framework.

Starting with SproutCore 1.0, you will have a few new options in the SproutCore build tools to let you output a version of your application just for the mobile phone.  Rather than force you to create an entirely new app for mobile devices, SproutCore treats platforms a bit like creating multiple languages.  That is, you simply add a “foo.platform” folder to your project.  Anything you place in that folder will only be included in the build for that platform.

For example, let’s say you were designing a Contacts application.  You might create some models and controllers for your app.  These will live in the models and controllers folder just like before.  But now you could also create a “desktop.platform” folder and a “mobile.platform” folder.  Inside of these folders, you can add the views and view-level resources needed to build the unique UI for that platform.  Here is how that folder structure would look:

contacts
  core.js <-- namespace
  models/
    contact.js  <-- Contact model object
  controllers/
    master.js  <-- typical controllers
    detail.js
  desktop.platform  <-- for the desktop browsers
    main.js <-- this main() is run when loading the app in the desktop
    english.lproj
      body.rhtml <-- view helpers for desktop
  mobile.platform <-- for the mobile browsers
    main.js <-- this main() is run when loading the app on the phone
    english.lproj
      body.rhtml <-- view helpers for mobile

When you run a build, SproutCore will output both the desktop and mobile directories in your bundles.  Once your app is deployed, you can visit your two apps by going to either:

Desktop: http://www.example.com/contacts/en/desktop
Mobile: http://www.example.com/contacts/en/mobile

There is also a facility to add auto-detection code at the top of your template page so you can redirect to the correct version of your app whenever a user visits your app’s main URL.

Of course, this means that the SproutCore JavaScript itself will also come in desktop and mobile flavors.  More on that as it develops.

This new feature in the build tools is still fairly primitive.  I’m sure we’ll be adding many more features to it once we get into your hands, but in general this opens up a new and very exciting area for SproutCore.  I can’t wait to see what you all do with it!

SproutCore 1.0: SC.Enumerable and SC.Array

One of the trickier aspects of making SproutCore DOM-library independent is dealing with enhancements these libraries make to built-in JavaScript objects.

For example, Prototype adds some very useful iterator methods - such as uniq() and compact() - to Array.prototype that we use frequently.  These helpers reduce our code size and make it easier to read.  On the other hand, depending on them limits your ability to use other libraries.  What if you want to load a library that defines these methods in a conflicting way?   For example, the way each() (or forEach() in the JavaScript standard) is implemented by Prototype, jQuery, and built-in by Firefox each pass different parameters to your callback.  Not pretty.

Some libraries, like jQuery, avoid modifying built-in objects altogether to work around this problem.  I didn’t think we needed to be that dogmatic about it, because some of these are so useful.  So I decided on a few simple rules:

  1. Never, ever, modify Object.prototype.  This breaks too many things.
  2. Many of the more useful enhancements that libraries add, such as forEach(), map(), and reduce() on Arrays, are actually part of the new JavaScript standard but just unevenly implemented.  SproutCore will provide implementations of these on all browsers that do not have them consistent with the standard.  You can basically use any of the iterators described in JavaScript 1.8 on any browser with SproutCore.
  3. A few of the more useful helpers defined by Prototype, such as compact() and uniq(), which have common, unambiguous functions, are provided by SproutCore as well.  They are implemented in the same way as Prototype so that if you do not want to.
  4. We will also enhance Array and String as needed to support property observing and localization.  For these methods, we try to name them in a way that is unlikely to conflict with other libraries.

While I was at it, I decided to make these enhancements available as mixins as well.  The SC.Enumerable and SC.Array mixins in SproutCore provide our uniform enumeration API.  They are applied automatically to Array, but you can apply them to any other object as well to make it “array-like”.   This opens up lots of possibilities for creating sets, sparse arrays, and other complex collection-oriented data structures that you can essentially pass in anywhere you might normally expect an Array.

Overall, I’m pretty happy with the results.  SC.Enumerable implements common iterators such as forEach(), map(), filter(), every(), some() and reduce().  These methods are all implemented natively in Firefox and some are in WebKit.  For all other browsers, SproutCore provides a generic implementation for you.  In addition, we’ve come up with some really useful extra features, such as:

SC.Enumerator

Sometimes you don’t want to just iterate through an array.  Instead, it is easier to pass around an “iterator” that can simply return the next value until you are done.  For example, let’s say you are going to process a bunch of data to produce some graph results.  This would potentially lock up the browser if you have a large data set.  It would be better to schedule a timer and then process them one or two at a time.  Here is how you could do this:

function processData(array) {
  var enumerator = array.enumerator(); // get an SC.Enumerator instance
  var process = function() {
    var next = enumerator.nextObject() ;
    if (next) {
      processItem(next);
      process.invokeLater(); // schedule to fire again
    } else process = enumerator = null; // cleanup closure
  };
  process(); // start processing
} ;

processData(aBigArrayOfData);

The nice thing about this is the enumerator will work even if you edit the array of data, whereas keeping a simple index would be more difficult to keep in line.  Note how you can call nextObject() on the enumerator until it reaches the end of the array, afterwhich it will return undefined.

Content Observing

One other benefit SC.Enumerable brings is it makes your array observable.  All arrays have a ‘[]‘ property which you can observe to be notified whenever the membership of the array changes.  For this to work, you must use the SproutCore-specific API for manipulating the array instead of the native methods, but this is cheap and yields huge wins.  Here is a banking example:

MyApp.account = SC.Object.create({
  ledger: [],

  ledgerDidChange: function() {
    console.log('account ledger changed!');
  }.observes('.ledger.[]')
});

With this controller, you could now insert or remove items like so:

MyApp.account.ledger.pushObject(newDeposit);
> "account ledger changed!"

MyApp.account.ledger.popObject();  // this is probably stealing. :-)
> "account ledger changed!"

MyApp.controller.records.insertAt(3, newWithdrawl);
> "account ledger changed!"

This makes it incredibly easy to observe changes on an array when you care about the member of the array itself.

Reduced Properties

Of course, we didn’t stop there.  Observing when an array value has changed is nice, but often times you don’t care about the array itself, only a summary of its contents.  For example, consider the ledger above.  Maybe what you really care about are not the specific transactions, but the sum total of the account.    You can get this with something called a “reduced property”.  A reduced property is simply a value that is computed dynamically by evaluating all of the items in an array and combining their results.  You can write your own reduced properties, but SproutCore comes with a couple of useful ones built in as well.

To get the value of a reduced property, you just use get() on the array with the property name.  All reduced properties begin with an @ symbol and may contain a parameter identifying a specific property on the array contents you are interested in.  For example, assuming all of the objects in the ledger above have an “amount” property that tells you their value, you could get a sum of the transactions with:

MyApp.account.ledger.get('@sum(amount)');
> (returns total amount)

Best of all, reduced properties are observable as well.  So you can bind to them.  If you wanted to show this account balance in a label view, for example, you could just bind the “value” property to "MyApp.account.ledger.@sum(amount)".

And so much more…

SC.Enumerable opens up a wide array (no pun intended) of possibilities for working with collections of objects in SproutCore.  Best of all, these features are available TODAY in the SproutCore version you probably have installed on your machine.  Checkout the SC.Enumerable and SC.Array mixins today.  They both get a minor facelift for 1.0, but for the most part they work now.  Get to know them.

SproutCore 1.0: SC.Builder

Over the past few months, I’ve worked more and more with jQuery while investigating removing our dependency on Prototype.  By far, my favorite part of the library is its API.  jQuery’s design genius is to use chained methods to operate on a result set.  This both makes your code very compact and it makes the API itself easily hackable as you can always add new helper methods at runtime.

This API-model makes a lot of sense for features where you need to configure a bunch of JavaScript (or DOM elements).  For example, let’s say you want to quickly find all of the headings on your page, turn them red and change their text.  It’s easy to do this in jQuery like so:

$('h1').title('Hello World!').css('color','red');

Pretty nice.  Of course, you can do a lot more complicated things with this as well, but you get the point.

Of course, this kind of API lends itself quite well to the DOM layer, but it got me to thinking about what other pieces of an API might be better expressed this was as well.  Take for example, Ajax requests.  When you think about it, an Ajax request is a one time action.  It’s really pretty clunky to have to create a Request instance and configure it just to send off a request.  We could do better.

In SproutCore 1.0, we will have a new SC.Request API that you can use to make low-level requests to a server. (This will sit beneath SC.Server, for example, replacing what you would do now with Prototype).  We’re still putting the finishing touches on this API, but its here is an example of how you might send a request to a server to get a resource:

SC.Request.get('/twitter/feeds').format(SC.JSON_FORMAT)
  .notify(Twitter.feedController, 'feedDidLoad').send();

Not bad huh?  This would request the named twitter feed, expecting a JSON format, and then notify your the Twitter.feedController when the request is finished.  Request object’s have a response property that contains either the retrieved JSON or an Error object depending on the result.

The nice thing about this model also is that you can hold onto the builder object and use it over and over again.  For example, this is how you would setup a controller so you can send the same request over and over again:

Twitter.feedController = SC.Object.create({
  feedRequest: SC.Request.get('/twitter/feeds').format(SC.JSON_FORMAT),
  refreshFeedThenNotify: function(target, method) {
    this.feedRequest.notify(target,method).send();
  }
});

You can call refreshFeedThenNotify() as often as you want and each time it will initiate a new request.  Pretty powerful for a few lines of code.

SC.Request is just one example of how we are thinking of using this DOM-builder-like API.  We are also planning API’s for configuring views, menus, and even models.  This is starting to look like a new design pattern, so we got to thinking: maybe we should provide some support for it.  So that’s what we’ve done.

Introducing SC.Builder

SC.Builder is a simple class that makes it terribly easy to create jQuery-like builders for any kind of content you want.  For example, to create your own jQuery all you need to do is:

jQuery = SC.Builder.create({
  init: function() { .. setup ... },

  // other helper methods you want here
});

Out of the box, this builder can do method chaining, supports plugins, and implements SC.Enumerable, which means you can iterate over it using typical Array methods like getEach().  All you need to do is to fill in the content-specific helper methods you want to add.

As we finalize some of these new API’s over the coming few weeks I’ll post more details about them, but I’m very excited about what SC.Builder makes possible.  Configuring your SproutCore app has never been so easy.

SproutCore 1.0: New Bindings and Observers

Property observers and bindings form the very core of SproutCore’s API.  In fact, SproutCore is one of the few frameworks to have implemented bindings and observers from the very beginning instead of bolting it on at a later date.  This contributes heavily to SproutCore’s compact design.

The centrality of bindings and observers also makes it one of our most important so that is why it was one of the first things we revisited for SproutCore 1.0.  Since this code as so old, it was quite easy to rewrite it using some more efficient techniques, giving us a 2x performance boost when creating new bindings and setting up new observers.

In addition to a raw perf boost, we also changed some key behaviors related to timing that could change how your application behaves.  These changes should speed up your app even further by reducing the number of bindings that fire in your application.  Depending on how you’ve written your code, however, it could change how your code behaves.  Here is what we did:

Change 1: setIfChanged Becomes Default

Today, every time you call set() on an object, SproutCore will invoke any observers you have as well, even if the value you are setting has not changed.  For example, if you write some code like this:

obj = SC.Object.create({
  firstName: null,

  firstNameObserver: function() {
    console.log("observer fired");
  }.observes('firstName')
});

obj.set('firstName', 'John');
> observer fired
obj.set('firstName', 'John');
> observer fired
obj.set('firstName', 'John');
> observer fired

Now, the default behavior has changed.  With the new code, observers will only fire if the value you set actually changes.  For example, the output of the above code with SproutCore 1.0 is:

obj.set('firstName', 'John');
> observer fired
obj.set('firstName', 'John');
obj.set('firstName', 'John');

This means you can write less code to make sure your observer is not fired more often than necessary.  Big win.

The only problem with this change is that this means computed properties may sometimes not be set more than once if you call set() with the same value.  Usually this will not matter, but if your computed property needs to be called everytime you try to set the value, you can change this behavior by adding the indempotent() helper to your property:

fullName: function(key, value) {
  // do something interesting here
}.property().indempotent(NO)

Adding this qualifier to your property will cause it to be called every time there is a set() instead of just when the value changes.

Change 2: Binding and Observer Timings

Currently, whenever a property changes on your application, observers are notified immediately unless you are already inside another observer handler.  Then they are queued and notified when the root observer exits.  Since bindings are implemented using observers, bindings will trigger when your other observers finish but before the set() returns, again unless you are more than one level deep in observers.

If this sounds confusing to you, that’s because it is confusing.  It also makes it hard to predict exactly how change notifications will propagate through your application.

With SproutCore 1.0, this timing behavior is significantly cleaned up.  The rules are simple:

  • Observers (i.e. methods you setup with observes() or addObserver()/removeObsevers()) will fire immediately when you call set(), unless wrapped inside of a beginPropertyChanges()/endPropertyChanges() pair.  In that case, they will fire immediately when you call endPropertyChanges().
  • Bindings on the other hand queue up.  They fire only once at the end of the run loop, when all of your normal event handling or timer code finishes executing.  Bindings are also coalesced.  If a binding changes more than once during a single run loop, it will still only fire once at the end.

The net effect of this timing change will be that when a property changes on an object, the observers you have written for that object will execute immediately so you can update other state within your object.  But, the bindings that tie together the objects in your app will fire all at once.  This will cause changes to relay through your app rather like a wave, starting at the model or view layer and propagating to the other side in layers.

This more controlled rate of change will generally not require you to change any code, but it does mean that code you put in to gate changes and control duplicate messages will be less necessary.  It will also generally speed up your application since binding change notifications occur less frequently.

Change 3: Cache Computed Properties

I mentioned this the other day so I won’t spend a lot of time on it now, but the basic idea here is that often the return value of a computed property only changes when one of its dependent keys or some other state changes.  In the mean time, multiple calls to get that property could return a cached value.  Caching used to require many lines of code, but now it can be done with just one.  Here is how you write a cached property:

contact = SC.Object.create({
  firstName: 'John',
  lastName: 'Doe',

  // computed property, cached until first or last name changes
  fullName: function() {
    console.log('computing fullName') ;
    return this.getEach('firstName','lastName').compact().join(' ');
  }.property('firstName', 'lastName').cacheable()
});

contact.get('fullName')
> computing fullName
> "John Doe"

contact.get('fullName')
> "John Doe"

contact.set('firstName', 'Jane');
contact.get('fullName');
> computing fullName
> "Jane Doe"

contact.get('fullName');
> "Jane Doe"

Now the fullName computed property will be called only once and cached.  You can get() as often as you like without paying the cost to recompute the value.  If you ever need to recache a computed property without changing one of its dependent keys, you can do so by calling properyDidChange().

Change 4: Use a target and method with addObserver() and removeObserver()

The low-level addObserver() and removeObserver() methods are used to setup and remove observers on an object.  Their API currently requires you to pass the property you want to observe along with a function to execute.  To execute the function in the context of a particular object, you had to wrap you function using Function.bind(), which adds a stack frame, another object and closure and generally slows everything down.

Now addObserver() and removeObserver() will be able to accept a target and method, which will be tracked separately for you.  This allows you (and SproutCore) to avoid having to create these extra closures, which increases performance and helps on limited JavaScript engines like IE7’s JScript.

Change 5: Disable Automatic Observer Notification

Normally, when you call set() with a new value on a property, SproutCore will automatically notify observers when the property has changed.  Sometimes, however, you may have a property with a more complex behavior and you don’t want the automatic notification process to take place.  Now you can override this behavior by implementing a new method on your class called automaticallyNotifiesObserversFor(key).  Returning NO for this method will disable automatic notification for the passed key.

This technique is most useful when used with computed properties.  For example, let’s say you decide to implement a computed property that only allows values greater than 10 to be set on it.  Here is how you might implement this:

obj = SC.Object.create({
  // computed property can be set to any number
  // as long as it is greater than 10
  value: function(key, value) {
    if ((value !== undefined) && (value > 10) && (value !== this._value)) {
      this.propertyWillChange('value'); // implement notification manually
      this._value = value ;
      this.propertyDidChange('value', value) ;
    }
    return this._value ;
  },

  // disable automatic notification for this property
  automaticallyNotifiesObserversForKey: function(key) {
    return (key === "value") ? NO : YES ;
  },

  // add observer just to show  what happens
  valueDidChange: function() {
    console.log("value did change to: %@".fmt(this.get('value')) ;
  }.observes('value')
});

obj.set('value', 20) ;
> value did change to 20

obj.set('value', 5);
[no output]

obj.set('value', 21)
> value did change to 21

obj.set('value', 21)
[no output]

This is definitely an advanced topic, but if you need to control precisely how and when property change notifications happen, it can be a god-send.

In general, you will be able to use the new property observers and bindings in SproutCore without having to worry about the changes here.  You should just notice your app running a little faster.  Understanding how some of these work though can dramatically reduce the amount of code you have to write in certain advanced situations.  If you have questions about this once its available, be sure to ask on IRC or the mailing list!