Nifty Trick with Root Controller Paradigm
I want to start this off by saying that I am constantly amazed by the power baked right into sproutcore. Most of the time what you need is there already, you just have to read the code and the comments.
With that said lets talk about this nifty trick shall we? First and foremost if you have not read Evin’s post about the “Root Controller Paradigm” click here, I will wait…
Ok now that you have that out of the way, let’s say that I am using this technique to build up a tree of objects to display in a source list. While I am building this tree I decide that I want to have some dynamically calculated value appended to the the end of the text in the collapsible header row. A good example would be a list of tasks and the header row’s text displays as the name of the task list. The dynamically calculated value would be based on the number of tasks, the effort each task will take, and it’s priority. The purpose of this value would be to display an estimated time-to-completion for this task list.
To do this you need to know when the effort and priority properties change on each task in this particular list ( the length of the list is already being observed for you because it is the content array of the controller ). Right at this moment you start trying to figure out how to observe these properties on each task in the list ( the content array of your controller building this tree of objects ). You might try adding the following method to your controller as I did.
someMethodName: function(){
/*
do something to recreate the tree
of objects to update the display
*/
}.observes('[]*effort')
The previous method will not work, although you and I both feel that it would because we have this idea that using the “*” wild card after the content array would allow us access to it’s contents. Now that I have you wondering why this does not work, here is the explanation. The content array has it’s own properties that can be observed, so using the wild card doesn’t really give you access to it’s contents.
You say on with the solution, well I’m here to tell you that the solution is fairly straight forward once you step back for a second to think about what you really want to do here. And now for the solution: We know that every item in the content array inherits from SC.Object ( if the objects are created using some construct bundled with sproutcore ). Because of this they also have the observable mixin applied and with this knowledge we have all of the tools we need to make this happen.
In your controller you have a method that builds each object in the tree of objects for your source list. In this method you have access to the the task list object and it’s array of tasks and their properties. This is how you are providing your header row text and calculated value. The observable mixin gives us the methods we need, “addObserver”, “removeObserver”, and “hasObserverFor”. With these methods we can now add observers to the effort and priority properties that will fire a method when they are changed and cause the tree to be rebuilt and the UI will display the result immediately. Here is the code:
for (var i = 0; i < len; i++){
task = tasks.objectAt(i);
// Removed usage of hasObserverFor via Charles' Comments
task.removeObserver('effort',this,'_contentHasChanged');
task.removeObserver('priority',this,'_contentHasChanged');
task.addObserver('effort',this,'_contentHasChanged');
task.addObserver('priority',this,'_contentHasChanged');
effortString = task.get('effort');
if(effortString &&
task.get('priority') !== CoreTasks.TASK_PRIORITY_LOW) {
// sum up effort for High/Medium priority tasks
effortMin = parseInt(effortString, 10);
var idx = effortString.indexOf('-'); // see if effort is a range
if(idx === -1) { // not a range
effortMax = effortMin;
} else {
// effort IS a range, extract max
effortMax = parseInt(effortString.slice(idx+1), 10);
}
totalEffortMin += effortMin;
totalEffortMax += effortMax;
}
}
if(totalEffortMin !== 0) {
var totalEffort = '' + totalEffortMin;
if (totalEffortMax !== totalEffortMin) {
totalEffort += '-' + totalEffortMax;
}
displayName = displayName + ' {' + totalEffort + '}';
}
return SC.Object.create({
displayName: displayName,
assignee: assigneeObj.assignee,
treeItemChildren: tasks,
treeItemIsExpanded: YES
});
The important parts here are lines 3-10. These lines check to see if the current task object has an observer for the properties that we would like to observe. If the task object does we remove the observer and then add new observers to observe the new value that these properties hold. These observes fire a method on the controller that rebuilds the tree, which will cause the UI to immediately reflect the changes of each task in the task list array.
Hopefully this will help you on your way and point you in the right direction. Just remember this, when you think you’ve hit a wall look to the comments and the code in sproutcore’s source and you will probably find the answer. If by some chance you are not able to find the answer there you can always look to this blog, as I will post any way that I find to get the most out of the sproutcore frame work. Don’t forget to drop into the #sproutcore IRC channel on irc.freenode.net.
1 Notes/ Hide
-
sproutcore reblogged this from joshholt
-
joshholt posted this