Blog

Have you ever used a rule qualifier like "object.employees.count > 0" in Wonder?  Object is whatever your current object happens to be, employees is a to many relationship off of object, and count is the count method on NSArray. Simple enough, right?

Have you ever tried "object.employees.isEmpty = 1" instead?  It doesn't work. You might wonder, why not??  count() is just a method on NSArray.  isEmpty() is as well.  Why does one work and one fail?  This has to do with how valueForKey is evaluated on NSArray.

With NSArray, when you call valueForKey, you call it on the array's objects instead of the array itself. So, when your qualifier key path is evaluated, anything after "object.employees." is evaluated on the objects inside the array. For example calling valueForKeyPath using "object.employees.salary" will produce an array of salaries rather than trying to call the method salary() on NSArray.  But wait! That means that instead of calling count on the array, count should be called on the objects instead.  So why isn't count failing just like isEmpty??

The answer lies in the Wonder NSArray implementation.  If you look at the source for the valueForKey method on Wonder's reimplemented NSArray, you'll see the answer.  The first thing valueForKey does is examine the key when it is non-null.  If the key begins with the operator character @, then it executes the key as an NSArray operator.  After that, it checks one more thing... is the key "count".  If so, it returns the array's count() method.

And with this information, we now have a solution to our problem. It turns out that Wonder provides us with an isEmpty array operator. So if you want to use the isEmpty method in a qualifier key path, you would use "object.employees.@isEmpty" instead.

This is great news, because if I understand Wonder's rule system caching correctly, object.employees.count will cache a new value every time the keypath evaluates to a new count.  With isEmpty, there are only two possible values to cache and compare. So using isEmpty for keyPaths beginning with "object" or "session" should result in faster comparison and a smaller memory footprint too :)

If you've used the ERDControllerButton you may have noticed your branch choices tend to cascade down into your embedded page configurations.  You can block this by using a rule like:

100: pageConfiguration = 'ListEmbeddedMovie' => branchChoices = ()

If you have lots of nested configurations in a page, that can be a bit of a nuisance.  However, there is another way.  You can prevent a branch choice from showing up unless it is explicitly named in the rules by beginning the method name with an underscore like:

public WOComponent _save(WOComponent sender) { ... 

Now, in order to see the _save action's button, you need to name it explicitly in a rule like:

100: pageConfiguration = 'EditRole' => branchChoices = ("_save")

D2W Tip

Q. If you're 3 switch components deep in your component hierarchy, how do you get your navigationState to make decisions like what branch choices should be available?

A. 100: session.context.page.d2wContext.navigationState = "Admin.EditUser" => branchChoices = ("choice1", "choice2")

Since each embedded component gets its own subcontext, it might seem impossible to get page state information like your navigation menu's state.  No matter how deep you go on the page though, you can always get back to the top this way. (smile)