By Daniel Wood, 13 March 2017
Search is an integral part of any solution, and can take many forms. FileMaker provides us with a useful native find mode, along with quickfind. Often though this is unintuitive for users, or just does not fit in with the design of a solution. In this article we present a slick, great looking search box UI design pattern, and explain how it is built. We also incorporate a number of native FileMaker techniques to achieve this (no plugins here!).
The primary focus of this article is on the construction of the UI design for our search box. Other techniques discussed involve using relationship level filtering of results (in a basic way) and having a timed delay on our search filter.
So, what does FileMaker offer in the way of searching? Well there is find mode obviously. Great power, but perhaps a little unintuitive for the average user, especially in this day of age where websites provide pretty commonplace design patterns for searching. Quickfind is a much nicer alternative, combining an automated 'find mode' search of sorts, with a single search box making it easier for users to search.
Both find mode and quickfind rely on results being displayed in a found set. If your solution is setup this way then great, you can stop here :) If you are into building solutions that don't feel like native FileMaker, and feel more like a native application, then read on.
Portals have been used to display search results for a long time, it's no secret. No matter how you end up collating your results for display in a portal, the portal generally allows you to select a record to then navigate to. The benefit to portals is that they can be placed within a layout, for in-line results display. This is perfect for the modern solution where you may have a search box at the top of every screen.
When you begin searching at imdb, matching results drop down underneath the search box. The size of the box shrinks or grows depending on the results found. If too many are found, a link at the bottom is provided to view the full results.
TV.com is also very similar. Results drop down displaying an image, information, and a few links to useful pages. The box resizes again depending on results found.
That was the goal. How close to this look and feel can we achieve in FileMaker natively. By that I mean no plugins, but I also mean no web viewers. I want this to be native to the point that making any changes to it's look and feel is achieved through using layout tools.
SO how did we do?
What you are seeing above is all native FileMaker. In order to make this usable on different layouts, it had to be easy to transport anywhere, and hook up for any given search box. A few points on this:
Pretty cool huh? We think so!
So what is actually going on here? Well, if you like to get stuck in and figure this out for yourself, there is a demo file at the bottom of this article you can grab and dissect til your hearts content. For those who would like a bit more information, read on.
Here is our search box setup in layout mode. We have numbered 6 key components to the search which we'll go through step by step to understand what they are and their role in creating the interface. Briefly what we have is:
And of course a portal to show the results.
We make use of the hide layout object condition to create the illusion of a box that shrinks in size as results are refined. Our lines are shown or hidden according to a set of rules to achieve this. The sliding panel is used to enclose all the objects we wish to hide en-masse (it's just an easy way to manage them).
Next we'll look at each object in a bit more depth.
The interface has a number of components, but two of them contain object names which are key. We use a naming convention for these:
There really isn't much to the search box. It's just a global field we placed on the layout to accept user input. We format it to look how we want.
The magic is applied to the search box through script triggers:
Quite a few aren't there? Fear not, only half of the triggers are concerning the user interface. The onObjectEnter and onObjectExit triggers are for deciding whether the results are to be shown or hidden. We always hide when the search box is exited, but we only show once the user begins typing. the onObjectKeystroke trigger is part responsible for searching, and part responsible for refresh of whether to show/hide results.
We will cover how the searching is done later on in the article.
While this isn't absolutely necessary, it's a really nice touch to tell the user how many results are found, and more specifically if nothing is found. We use a button bar so that we can place the calculation for display direct on the layout in this object.
We won't bother showing that calculation here, you can check it out for yourself in the demo. Basically it looks at how many records are found through the relationship to our results, and displays a text string based on that number, pretty simple stuff.
Now things get interesting! Our interface has 3 key horizontal lines which we'll call top, middle and bottom. The purpose behind all three is to show one of the 3 at any given point in time, hiding the other 2. The lines act as the bottom of the results box. There are three scenarios for when these lines show.
In the case of our top line, this will act as the bottom of our results box if there are NO results. In this case, we just show the "no results found" display text, and our box ends there.
So conversely we hide this line when there ARE results.
So now that we know our lines are to show the bottom of the box, can you think of when we would have this middle line act as the bottom of the box? I'll give you a clue, this line sits within a portal row at the bottom of the row.
Got it yet? How about if we only had 3 results found? Or 2? or 1? yes! This line is used as the bottom of our box when we have fewer results found than the number of default rows of our portal.
In our demo file, our portal is setup to show 7 rows. If we only find 3 results, then we want the bottom of our box to end at the bottom of the third row.
So, our rule for this line is that it is to be shown if there are results found, and where there are fewer results than our portal default. Or the converse of this: We hide it when the results are more than the number of portal rows.
Because this line sits inside the portal, we don't need to bother with the condition of whether there are results or not, this is going to be hidden if there are no results. However it will display on every portal row for which we have results, hence we must hide it on all bar one.
Ok so hopefully with top and middle out of the way, the reason for the bottom line should be obvious. If we have lots of results (more than the number of portal rows by default) then we want the results box to be the bottom of the portal. So this line sits just underneath the bottom of the portal.
If there are more results found than the portals default, we show this line, otherwise we hide it.
This is a transparent, no bordered single panel control. We use this so that we can enclose everything else inside it, and then we can just apply a single hide condition to the panel to show/hide everything inside.
The design for our results panel dictates that if the user is not inside the search box, then we will not show any results display. If the user is inside the search box, we only show results content if they have typed at least one character, otherwise we show nothing.
Showing or Hiding of the results panel is done by way of scripts that execute when the user both exits and enters the search field. Recall our panel has an object name, so showing/hiding of this is performed by simply refreshing the panel object (which triggers it's hide condition to run).
Good! We're almost there. Hopefully you can follow the reason behind the design components and why we show/hide certain elements under certain conditions. But how do we hide things?
We wanted this solution to be completely transportable and applicable to any other search box in your solution with as minimal effort as possible. To that end, we built a custom function in which all logic for showing/hiding UI components is contained.
This is our custom function called @SEARCH_ShowHide. The function takes 4 parameters:
When we setup a hide condition on one of our 4 main hiding elements, we simply reference this custom function. Here is an example of the hide condition on the middle horizontal line:
@SEARCH_ShowHide ( "search" ; "line_middle" ; 7 ; Interface_CUSTOMERS__results::id_customer )
We have told it the name of our search box, what component we are hiding, how many portal rows we have for results, and what the results primary key is.
We're not going to post all the code for the custom function here, you can look for yourself within the Demo File, but we'll point out a couple of things:
Here's what is going on:
When you put it like that it's really quite simple. There are a number of components but on their own they're all pretty simple and easy to see what role they play.
Building this into your own solution is simple. If you already have results being shown in a portal, then you're all set. Just paste into your solution and change the following:
This demo file has used filtering of results at the relationship level, and we do not use portal filtering. Digital Fusion has always had a policy of using relationship filtering where possible due to substantial performance gains to be had when working with larger data sets.
That is not to say portal filtering is bad, nor we never use it. For this demo, if you wish to use portal filtering then you will need to make a few modifications to the solution. Our demo checks the number of records found through the relationship to determine the number of results, and apply hide conditions accordingly.
The number of results found through portal filtering is a little different to obtain, as the relationship record count may differ from the portal record count. One technique you can use is to add a COUNT summary field to the results table, and place this inside the portal with an object name attached. You can then use GetLayoutObjectAttribute ( "ObjectName" ; "Content" ) to obtain the found count. This can be inserted into the hide custom function in place of where we count records found.
We won't go into much more detail into this here, suffice to say it is a change that can be made to use this technique with portal filtering.
Our search box uses script triggers to carry out when the reevaluation of the relationship occurs. This is a pretty common technique used to produce dynamic search results for years in FileMaker, ever since v10 introduced triggers.
We take this one step further. The traditional approach is to refresh search results after each keystroke. This can suffer from performance issues, specifically when there are many results found, or the user is over a WAN connection. THe user often finds themselves being unable to continue typing their search term while they wait for results to load. This can compound itself when results begin showing immediately after the first character is typed - as often the most results found are at this point in the process.
Our solution is to introduce a short timed delay before filtering occurs. We use an onTimer to control this delay. Every time the user enters a keystroke, we reset the onTimer which resets the delay. Once the user ceases typing, the delay occurs and after that the onTimer script runs, which carries out the search.
To give a basic illustration, consider the user typing the word "David" into a search box:
If the user stopped typing at the "V", then filtering would occur there. If they continued typing the rest of the word, filtering would again occur 0.5 seconds after the last "D" was entered.
We actually wrote an article on this technique a few years back now. You can read it here. The general techniques in that article are exactly the same as they are in our demo file now. In our current demo file we have simplified some things such as script parameters just to make this file easier to understand.
Because this technique uses an onLayoutKeystroke trigger after each keystroke, we can intercept certain characters and change the resulting behaviour. This is exactly what we have done when we detect the escape character is pressed by the user in the search field. In this case, we clear the search field contents. This is a nice intuitive way to clear the field rather than providing an X icon.
This is another one of those "many ways to skin a cat" things in FileMaker. Some prefer using portal filters, others relationship level filtering. At the relationship level, some build big keys, while others opt for exact matches.
Exact matches in this case are pretty useless, and keys that are built are often big and cumbersome.
A few years ago now, Danny Kohn on the webiste FileMakerInspirations.com wrote a very insightful article into this problem, and posed a technique using the ≤ and ≥ predicates of relationships, and the sort ordering of text in FileMaker to achieve a relationship filtering technique that results in a very lightweight key field.
You can read his article in full here. It covers the technique really well and so we won't go into much more detail on it in this article, suffice to say our demo file makes use of this technique.
What good is an article like this without a demo file to accompany it right? Well here it is! No username and password, fully unlocked just go for it.