Building a Super Slick Search UI

By Daniel Wood, 13 March 2017

 

Introduction

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.

 

A bit of Search Background

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.

 

The Search Box Design

The inspiration for this design was partly borrowed from websites and how they achieve search boxes.  Two examples that come to mind are imdb.com and tv.com

 

imdb

 

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

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.

 

The Goal - Build This in FileMaker

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?

Demo

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:

  • We give the appearance of a results box that shrinks in size as fewer results are found.
  • We can display the number of results found, or 0 if none are found.
  • Results are scrollable.
  • Results dynamically show as the user types.
  • The results box only shows after the user beings typing.
  • The results box disappears if the user exits the search field.

Pretty cool huh? We think so!

 

Breaking it Down into Components

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.

Components

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:

  • The search box
  • A button bar
  • A top horizontal line
  • A middle horizontal line
  • A bottom horizontal line
  • A single sliding panel

 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.

 

But First Some Naming Conventions

The interface has a number of components, but two of them contain object names which are key. We use a naming convention for these:

  • The search box can be named anything you want, but we've called it "search"
  • The sliding panel used to enclose the rest of the search UI is named <searchfield>.panel. So in our case, it is called "search.panel". We use this convention so that scripts can determine what the object panel name is based on the search fields object name. It saves any hard-coding or parameter passing of object names.

 

The Search Box

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.

search box

The magic is applied to the search box through script triggers:

Screen Shot 2017 03 12 at 3.00.37 PM

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.

 

Button Bar Calculation for Results Display

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.

 

Top Horizontal Line

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.

 

Middle Horizontal Line

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.

 

Bottom Horizontal Line

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.

 

The Results Panel

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).

 

Still with us?

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.

custom function

This is our custom function called @SEARCH_ShowHide.  The function takes 4 parameters:

  • _searchObjectName:  This is the object name of our search box
  • _hideObject: This is a keyword that identifies which of the 4 hiding components we are trying to hide (top/middle/bottom line or panel).
  • _displayRows: How many rows does our results portal show by default
  • _resultsKey: This is a reference through to a primary key on our results table, and we use this to determine various bits of information around how many results were found.

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:

  • We count the number of results using Count function. This can often be not ideal especially if large numbers of results are found. You can always replace this with our technique found here in an earlier article by adding a foundcount calculation to your table. We opted to not do this here in order to keep the solution free of needing to add extra fields to your tables.
  • We make use of the dual nature of custom function parameters being both a value AND a reference to a field (big thanks to Wim Decorte of Soliant for his brilliant and invaluable article on the technique here). Using this technique we can count records within the custom function.

 

So Putting it all Together Again

Here's what is going on:

  1. User enters into search field. If there is a search, we run a script to show the results panel if not we keep it hidden.
  2. User beings searching. We show the results panel. Hide conditions determine the size of the results box
  3. The user clicks upon a search result to navigate away, or exits the search field. We run a script to hide the results panel.

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.

 

Applying to your own Solutions

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:

  • Change the parameters in the hide conditions on the 4 main objects to suit your solution
  • Make sure your table has a primary key (a unique value) to use, if not then shame on you!

 

A note on Portal Filtering versus Relationship Filtering

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.

 

Delaying the Search for Performance

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:

  1. "D" is typed, an onTimer is started at 0.5 seconds
  2. "A" is typed quickly after, the onTimer is reset back to 0.5
  3. "V" is typed, slower this time, but still only 0.3 seconds after the "A". The onTimer is reset to 0.5
  4. "I" is typed quickly, again the onTimer reset back to 0 .5
  5. "D" is typed, the user is done typing. The onTimer delay expires after 0.5, and the script runs,carrying out the filtering.

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.

Going back a couple of years now..

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.

Using escape key as a quick clear

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.

 

What about Relationship Filtering?

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.

 

Demo File

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.

ModernSearchUI.zip

 

Something to say? Post a comment...

Comments

  • Daniel Wood 27/04/2017 10:37am (3 days ago)

    Ah yes, very observant Mr Zakary :) I had come across this building the demo file, and figured I'd just leave it out ;) It is a bit of a nuisance for sure, and something that would be nice to have changed in future versions of FM. It's not ideal but you could place the sliding panel with the search results in the part underneath the top nav providing there is not too large of a gap under the search box.

  • David Zakary 27/04/2017 9:33am (3 days ago)

    Another top notch demo Mr. Wood.

    My only complaint, and this is a FM thing, not you - ideally the widget goes at the top of the layout - which might be a Top Navigation or Header part. The search results won't display across parts if that part is Top Navigation. It does work if it is a Header.

  • Daniel Wood 27/03/2017 10:55am (1 month ago)

    hi John,
    thanks for the comment!

    In regards to the middle horizontal line. This is a line that is sitting inside the portal row. So this means without a hide condition on that line, you would see it appear on every portal row which is not ideal and not what we want to use it for. We want to use this middle horizontal line when we have fewer results found than the default number of rows that the portal displays. So if the portal shows 10 results, and we only have 5, then this is the time we want to show that middle horizontal line.

    Again though, in this scenario above we only. want to show that line on one of the 5 results rows, in this case row #5 because this is the bottom of the results box. Rows 1-4 we do not wish to show the line.

    Thus putting it all together the hide condition for this middle row is to HIDE it when either the number of results found is equal to or greater than the default visible rows, OR in the event the number of results is fewer, we want to HIDE it on every row that is not the last (so rows 1-4 hide, row 5 show..)

    Hope that clears it up, using the word "bar" when talking about lines was probably a poor word choice!

  • John 27/03/2017 9:46am (1 month ago)

    Daniel,

    Great tutorial. Probably the best I have seen to date breaking out the individual elements and layers.

    I have a question for you: At the *end* of the "Middle Horizontal Line" paragraph you say "However it will display on every portal row for which we have results, hence we must hide it on all bar one."

    I am confused about what you mean by "...all bar one"

    Thanks!

  • stryker 17/03/2017 7:39am (1 month ago)

    nvm I think I may have figured it out. looking at the scripts now
    cheers!

  • stryker 17/03/2017 7:23am (1 month ago)

    Is anyone able to view this search bar under the hood? I cannot seem to edit the layout when opening the demo file.

  • John Mark Osborne 17/03/2017 6:49am (1 month ago)

    Very nicely executed!

  • Daniel Wood 14/03/2017 3:49pm (2 months ago)

    hi Shawn, thanks for the comment. Yes I've done a couple of solutions that have made use of selector connector as a means to provide a universal search context, big time saver for copy/pasting onto layouts. Other solutions the search box can be context dependent on the screen you are on, so horses for courses I guess :)

  • Shawn A. Krueger 14/03/2017 3:46pm (2 months ago)

    Great stuff, as usual. Thanks for sharing!

    Have you played with using this, combined with Selector Connector, to make your search results portal less context dependent? Could be a time saver if you are searching from multiple contexts.

  • Michael Sloper 14/03/2017 6:13am (2 months ago)

    Great demo. I like your use of the install OnTimer for .4 with the modify trigger. Thats slick.

RSS feed for comments on this page | RSS feed for all comments