DerbyJS: refList-wrapper update

27th of June, 2012 08:40 AM

A while back ago I created a wrapper for Derby's model.refList method. I felt this was needed because the existing native implementation of refList is cumbersome to use and require a lot of boilerplate code, while usually where ever I used refList I needed the same boilerplate code. Today it's time for an update.

Find the updated version at my GitHub repository here. Note that at it's current state, my refList-wrapper utilizes underscore, which you can find here.

RefList-wrapper what?

One very useful method/function of Derby is refList, which basically turns a model object collection into an array before passing it to the view. This is extremely useful because it is not possible to loop through an collection in a view, while it is possible to loop through an array using {#each}. The smart thing about refList is that it only references the values, so when updating the original model, the array created by refList will automatically be updated and you get all the pleasent DerbyJS magic (synchronization) we all love so much.

So why do we need a wrapper for the Derby-native refList? The docs of refList specifies the following about refList:

fn = model.refList ( path, to, key )

path: The location at which to create a reference list. This must be a private path, since references must be declared per mode

to: The location of an object that has properties to be mapped onto an array. Each property must be an object with a unique id property of the same value. May be a path or scoped model

key: A path whose value is an array of ids that map the to object's properties to a given order. May be a path or scoped model

fn: Returns the function that is stored in the model to represent the reference. This function should not be used directly.

So, basically if we subscribe to or fetch any data from our store and want to create an array of all of the results, but the data is stored as a collection rather than array. We will have to do something like this:

...

// Our data is stored like:
// users: {
//   "1": { -- The id of our user
//     name: "Carl",
//     password: "mysecretpassword"
//   },
//   "2": { -- The id of our user
//     name: "Homer",
//     password: "password"
//   }
// }

... 

// Fetch all users by path 
model.fetch('users', function( err, scoped_users ) { 
    
    var 
      temp_array = []; 
    
    for( key, scoped_users.get() ) {
      
      // Loop through each user and save every id to a temp_array 
      temp_array
        .push( key );
      
    }
    
    model.set(
        '_users_id_list', // A temporary name which becomes 'key' in our refList         temp_array
      );
    
    model.refList(
        '_users', // Must be private path - ie. start with _. This is the object we can use later in our view as an array
        'users',
        '_users_id_list'
      );
    
  }); 

// Then we can do the following in our view. The same result would have been impossible to achieve without introducing a view helper or using refList

...

< h3>List of users
< ul>
  {#each _users}
    < li>{.name}
  {/}
< /ul>

...

While this is useful, because I tend to want to render lists quite a lot with all the objects I subscribe to/fetches, I figured it would be awesome if I could just do something like this:

...

// Our data looks like above

... 

// Fetch all users by path 
model.fetch('users', function( err, scoped_users ) { 
    
    model.refList('users');
    
  }); 

// Then we have the same view as above

...

If I only send in one parameter consisting of the path to the original model data, I simply want refList to create the referenced list as a private version of my parameter path, ie. "_" + parameter. I do not really care much for the array of the ids, so it can be hidden away. This is basically what the refList-wrapper does and I will then end up with above example working. Of course, I still want to be able to access the original refList, so if I send in all three arguments, it will work as the Derby-native refList. Also, If I specify two parameters, I simply want refList to use my first parameter and second parameters as expected, although just hide away the array of ids. In other words, I would prefer if refList worked as follows:

 

fn = model.refList ( [path, ] to [, key] )

path: The location at which to create a reference list. This must be a private path, since references must be declared per mode. Optional. Defaults to the "_" + to parameter.

to: The location of an object that has properties to be mapped onto an array. Each property must be an object with a unique id property of the same value. May be a path or scoped model

key: A path whose value is an array of ids that map the to object's properties to a given order. May be a path or scoped model. Optional. If not specified, a path will be generated consisting of all keys of the object at path to.

fn: Returns the function that is stored in the model to represent the reference. This function should not be used directly.

 

What has changed in my update?

In my original version of refList, it could in certain cases create a circular reference to my wrapper, which whenever called create an eternal loop (not good). This has now been fixed. Bascially what happened was, whenever you would call your routes client-side, refList would add an additional call to itself. Whenever refList was subsequentially called, it would break your page because it would cause an infinite loop (and eventually cause an Uncaught Error: Maximum stack overflow or something similar). 

I hope you will find my refList-wrapper useful and if it causes you any trouble, let me know and I will see what I can do to help. Please also do not forget that my refList-wrapper at moment is dependent upon underscore which you can find here.

The Weblog

Carl-Johan Blomqvist