This post builds on the session I presented at DevCon 2015: Data Modeling Beyond Anchor Buoy which described the FileMaker Selector-Connector pattern for the relationship graph. At DevCon we also went through some sample files which are available here. We’ll reference those files in this article as well. (You can also think of this as a continuation of my the earlier post on Selector-Connector: “FileMaker Data Modeling with Selector Connector“.)
DevCon 2015 was an exciting time for the Selector-Connector Model as Todd Geist won the Excellence award for both developing and formalizing the idea. I was very fortunate to have the opportunity to collaborate with Todd as the idea was developing. This collaboration lead to the current model used in our SeedCode Complete template, and was the basis of my DevCon presentation.
Why A New Model?
On of the things I wanted to stress in my DevCon session is that there was never an intention to come up with something new in the abstract sense of a “model”. Todd and I both had a specific problem we wanted to solve: we wanted to build portable choosers (aka pickers and selectors), e.g. a chooser that we could easily move from layout to layout. Not only would this make our development go considerably faster, but in the case of SeedCode Complete, our customers would be able to take advantage of this portability as they customized and expanded the template.
As it turns out, there are other benefits to the Selector-Connector that emerged, but focussing on the chooser is arguably best way to understand Selector-Connector.
An Anchor Buoy Chooser
As I mentioned above, there was no intention to introduce a new data model or replace Anchor Buoy (and indeed, Selector-Connector doesn’t replace it). Anchor Buoy (AB) has served me well since the graph was introduced in FileMaker 7, and even if I was going to bend some of the AB rules, I would do so as little as possible. With this in mind, I started developing the newest version of SeedCode Complete using the AB model and intended to take it as far as I could. This also had some additional practical appeal as it could lead to a clear process for “retrofitting” an existing AB solution with any new benefits that emerged. What I started with very much resembled the demo file aABChooser.FMP12 (from “Selector-Connector Example Files” here); a basic file with Contacts, Projects and Invoices. Projects would allow for multiple project contacts, so I would build the chooser there first.
If we look at the graph for aABChooser.FMP12, we see that it is still following the AB model. The table occurrences (TOs) needed for the chooser have been added as buoys to the Projects anchor. If we wanted to maintain a strict Anchor Buoy model, then adding a chooser to Invoices would require adding duplicate versions of these TOs to Invoices as buoys. This is, in fact, the model that the previous version of SeedCode Complete followed, and although certainly better than starting each chooser from scratch, it still required a fair amount of additional development effort for each chooser. This was as far as we could take the AB model in our goal for a truly portable chooser.
A Universal Context Chooser
In the file bUCChooser.FMP12, we’ve moved our chooser TOs from being Project buoys to a new position at the top of the graph. We’ve created a new table called UC; this is for Universal Context which was the working term both Todd and I were using for “Selector-Connector” at the time. This new table would consist of one record and a few global fields for managing the chooser. All the anchors would now be related to this new table via an x-join relationship with globals on both sides. I knew I wanted to use globals, so I didn’t trip any downloading of indexes, and only the x-join relationship supports globals on both sides.
Happily, it’s very easy to make this change from aABChooser.FMP12 to bUCChooser.FMP12. Even though we broke the relationship between Projects and the ContactChooser TO, the script references to the TO itself do not change. When we reconnected ContactChooser to Projects via the UC table, all references to ContactChooser from the Projects context remained intact. Only the global fields that were moved from Projects to the UC table needed to be “re-referenced” in our scripts and calculations.
One of the benefits to this new model that was not planned, but soon became apparent, was that the global fields that now live in UC would have usually needed to live in the anchor tables in an AB model. By introducing the UC table, not only were we working towards a shared chooser, but we were also able to take a whole class of global utility fields out of the anchor tables and consolidate them in the new table.
We now had a working chooser for Projects that should be easy to use from any additional anchor layout; we would just need to add an x-join relationship from the new anchor to the UC TO. However, before we proceeded to hook up our other anchors we wanted to get our chooser fully functional. This meant not only being able to select an existing Contact, but to use the chooser to create a new Contact from within the chooser.
“Pop-Back” and The Selector
Adding the ability to create a new Contact from the chooser turned out to be remarkably easy, and this was really the point where I realized we were onto something cool.
As we all know, FileMaker allows the ability to create new records through a relationship. And any fields that are set up as match keys in the relationship force the newly created child record to inherit the values for these fields from the parent, thus making sure the relationship remains valid after the new record is created. However, less well known is what happens if the parent value for one of the match fields is empty. If the child field is set to auto-enter a value, then that value will be “popped back” to the parent record. Presumably this is FileMaker’s attempt to maintain the relationship after the creation of the child record. “Pop-Back” is the term I learned for this behavior. Our friend Kevin Frank refers to it as the “Magic Key” which I’ve also always liked.
For our creating new Contacts in our chooser, this behavior works perfectly. If we want to create a new record, we just need to add a new global field to the UC table and then add a creation relationship to Contacts with the global field and the Contact’s primary key (shown below). We then need to make sure our global is empty and expose the fields from this new relationship in our selector (editable fields like FirstName, LastName, etc.). As soon as the user types into these fields, the new record is created and the primary key value is popped back to the global.
Although there’s no editing involved with our chooser, it was clear that this relationship could also be used for editing an existing record from any context as well. Rather than clearing the global field, we’d set it to the primary key of an existing record we wanted to edit. This is where the language and term for the “Selector” started to take form, and Todd deserves the credit for clarifying the naming pattern. The central table with the global fields becomes the Selector. This name fits well because it’s the globals in this table occurrence that you manipulate to form a relationship with the “Selected” record. The table occurrences to the right become the Selected, i.e. SelectedContact. With this naming convention, the workflow becomes very clear:
- Set the global in the Selector to edit a specific record
- Clear the global in the Selector to create a new record
- The “Pop-Back” behavior means no additional steps are required for record creation
Applying these changes, we now have our file cSelector.FMP12 which allows us to search and add multiple Contacts to our Project records using the Selector Model. We should now be able to easily add our Contact chooser to Invoices simply by “hooking-up” our Invoice buoy to the Selector, but we have one more issue to solve first.
Finish In Context
Although we’ve gone a good ways to make our chooser universal, we still ultimately need to write our chosen record somewhere and in a specific context. Using indirection is one possibility. We could detect our context and then write to the appropriate key field, something like:
Set FieldBy Name [ Get(LayoutTableName) & “::id_Contact” ; $sc_SelectedContactID ]
However, I’ve found these types of script steps somewhat fragile and difficult to support. We also have a different process in the case of Projects where we need to create a join record, so my single script step would invariably need some branching in addition to the indirection, and I think that’s too much complexity. For SeedCode Complete, I opted to use separate scripts for each context and then attach one of these unique scripts to the “save” buttons in that context’s chooser. This way, you could copy and paste the chooser from one context to another, and just change the save buttons. This is very much like what’s set up in the cSelector.FMP12 file. We have specific scripts for Saving the contact to both the Projects and the Invoices. All the other chooser scripts are universal.
There are other ways to do this final step of specifying where to write the newly chosen record:
Todd uses an interesting method where he adds an invisible button to the chooser and then in his chooser’s scripts he adds a step to go to this invisible button. The idea is to add the context specific script as an on-enter script trigger to this object. Todd calls these Virtual Script Triggers. This way, the scripts that work with the chooser never change, and you delegate the context specific logic to triggers. It doesn’t really save any lines of code, but it’s a very good way of separating the logic.
The Natural Hook
One of the things I accidentally stumbled across was some “natural” script-hook behavior. One of FileMaker’s behaviors is that when exiting a pop-over, the pop-over button always becomes selected. This behavior used to annoy me and I found myself always adding Go To Object steps, or changing the selected styling of the pop-over button so that you wouldn’t notice it. However, I realized that since I could count on it to act this way, the script trigger could be added to the pop-over button itself. I really like this because you don’t need to name the object with the hook attached to it. This means you don’t need the Go To Object step and (more importantly) you don’t have to worry about your object names breaking and morphing into something like “ChooserHook 2 Copy 3 Copy 2”. You can see the natural hook in action in the file dSelectorHook.FMP12.
FileMaker Select0r Connector: Does It Work?
Now that we’ve established our method for writing our chooser results back to context, there’s nothing left to do but the final steps to add the chooser to invoices. For the final file eSelectorFinal.FMP12, we just need to add our cross join relationship between Invoices and the Selector. We then copy and paste the pop-over button that encloses the chooser from Projects to Invoices and re-style it to fit the new layout. Lastly we add the new hook to the pop-over button as an on-enter trigger. The script we’re calling here is simpler than the one in Projects since we’re just writing back to the Invoice foreign key rather than creating a join record. Back to Browse…and it works! Our chooser is now highly portable and we’ve established a pattern for building new choosers for different entities.