Skip to content

Select

Iridium provides a basic select component to allow your users to search through and select from a list of elements.

You can create a select option simply using

go
FormSelect("cities")

Common Methods

For a list of common field component methods, see here.

Getting Started

Map Ordering

Unsorted Maps

Go does not provide a built-in data structure for maintaining insertion order into maps.

Iridium has it's own ordered_map collection to solve this issue. This means you're required to use or satisfy our ordered_map struct/interface and pair interface, to populate a list of dropdown items with a particular order. If you don't care about order, you can pass in a simple map[string]string as well.

Creating an ordered map

Singularly create your ordered map:

go
import "github.com/iridiumgo/iridium/core/collections"

myOrderedMap := collections.NewOrderedMap[string,string]()
myOrderedMap.Set("toronto", "Toronto")
// ... add more if desired

Bulk insert ordered map:

go
import "github.com/iridiumgo/iridium/core/collections"

pairs := []collections.Pair[string,string]{
	{Key: "toronto", Value: "The 6ix"},
	{Key: "vancouver", Value: "Vancity"},
	{Key: "montreal", Value: "Sin City"},
    {Key: "calgary", Value: "Cowtown"},
}

myOrderedMap := collections.NewOrderedMapFromPairs(pairs)

(Also supports spreading via NewOrderedMapFromVPairs)

Options

To set an ordered list of options, you can use the following methods:

go
// static
FormSelect("cities").
    Options(myOrderedMap) // <- look up a section on how to create

// callback
FormSelect("cities").
    OptionsFn(
        func(ctx FieldContext) collections.IOrderedMap {
            // This is alow a great spot to access your database
            // or a model from the `ctx` variable to set your options!
            om := collections.NewOrderedMap[string,string]()
            om.Set("toronto", "Toronto")
            return om
        }
    )

Preloading Considerations

By default, when using any of the Options methods, all options are sent directly to the client and searched client side.

This is nice for reasonably sized options, but is not recommended for very large number of records. To preload a few items, and then allow users to search server side, see our SearchResultsUsing method.

Options Random

As discussed above, you are able to provide a basic map[string]string if the order of elements is no concern:

go
// static
FormSelect("cities").
    OptionsRandom(map[string]string{"toronto", "Toronto"})

// callback
FormSelect("cities").
    OptionsRandomFn(
        func (ctx FieldContext) map[string]string {
            return map[string]string{"toronto", "Toronto"}
        }
    )

Multiple

You can allow users to select multiple options using the Multiple method as so:

go
// static
FormSelect("name").
    Multiple()

// callback
FormSelect("name").
    MultipleFn(
        func (ctx FieldContext) bool {
            return true
        },
    )

Setting up Multiple

Keep in mind, your targeted struct field should be []string to properly deserialize your multiple options properly.

If you're using a GORM driver, keep in mind it does not natively support []string as a struct field.

Tips

  • By default, cmd+click will remove an option you click. You have the option to clear all options, or click an option's X icon to deselect your row as well.

Searchable

Searchable allows you to search your dropdown by providing an input box in your popover:

go
// static
FormSelect("name").
    Searchable()

// callback
FormSelect("name").
    SearchableFn(
        func (ctx FieldContext) bool {
            return false
        },
    )

Searchable Notes

  • If you've enabled SearchResultsUsing, your search box will perform a backend searching using HTMX.
  • If you've provided Options instead, your search box will perform a client side search using Alpine.Js

Scrollable

Scrollable enables your select options popover to be a scrollable area, rather than showing all your options on the screen. This is highly recommended if your options will be longer than a handful.

go
// static
FormSelect("name").
    Scrollable()
    
// callback
FormSelect("name").
    ScrollableFn(
        func (ctx FieldContext) bool {
            return true
        },
    )

Nullable

Nullable will allow users to deselect options once they've been selected. This option is always true if multiple is enabled.

Users can deselect an option by click on their selection in the dropdown, or by clicking the X icon in the dropdown button

go
// static
FormSelect("name").
    Nullable()
    
// callback
FormSelect("name").
    NullableFn(
        func (ctx FieldContext) bool {
            return true  
        },
    )

PlaceHolder Message

You can provide a placeholder message to display in your dropdown if a user has not made any selections as so:

go
// static
FormSelect("name").
    Placeholder("Please make your selection!")
    
// callback
FormSelect("name").
    PlaceholderFn(
        func (ctx FieldContext) string {
            return "Please make your selection"       
        },
    )

No Results Message

You can provide a message to display if a no options are present in your select dropdown (either due to no options, or a search returning no options):

go
// static
FormSelect("name").
    NoResults("Hey! No results were found. Sorry!")
    
// callback
FormSelect("name").
    NoResultsFn(
        func (ctx FieldContext) string {
            return "Nothing here!"
        },
    )

Search Results Using

OptionLabelFromValue is required when using this method.

The SearchResultsUsing method allows your user to search in your dropdown on the server side. This is excellent for a large number of records.

The callback you create will fire whenever a user searches in your dropdown box.

go
// with order
FormSelect("names").
    Searchable().
    SearchResultsUsing(
        func (ctx SearchContext) collections.IOrderedMap[string,string]{
            users := GetUsers(ctx.Search)

            // Create an ordered map to guarantee option order 
            userPairs := make([]collections.Pair[string, string], len(users))
            for _, user := range users {
                userPairs = append(userPairs, collections.Pair[string,string]{
                    // We'll use their name as the key. The Value can changed to anything for display purposes.
                    // We're captializaing their names as an example.
                    Key: user.Name, Value: strings.ToUpper(user.Name),
                })
            }

            return collections.NewOrderedMapFromPairs(userPairs)
        }
    )

// get users utility function.
// As an example, we're query our Users' gorm models.
// And filtering their names based on a ILIKE query + a user's search
func GetUsers(search string) []models.Users {
    var users []models.User

    // Query the first 20 users whose email contains the search term
    if err := gormDBPtr.
        Model(&models.User{}).
        Where("name ILIKE ?", "%"+search+"%").
        Limit(20).
        Find(&users).Error; err != nil {
        // Optionally log/handle the error and return empty options
        return []models.Users{}
    }
    return users
}

Search Results Using Random

OptionLabelFromValue is required when using this method

If you do not care about the ordering of elements, you can also use the random method variant.

go
// random order
FormSelect("names").
    Searchable().
    SearchResultsUsingRandom(
        func (ctx FieldContext, search string) map[string]string {
            var users []models.User

            users := GetUsers(search)
            
            // Build options map in random order (value -> label)
            options := make(map[string]string, len(users))
            for _, u := range users {
                options[u.Name] = u.Name
            }

            return options
        }
    )

Option Label From Value

OptionLabelFromValue is a method that is required for any SearchResultsUsing method and your DefaultValue method.

Iridium needs you to specify how to find the label for any option. This is not required when using an Options method as you already provide all the required labels.

go
// Here, please assume we're working on a form for a `User` model
// Therefore, while the key of your dropdown options is the `User` ID,
// Iridium still needs to be told how that value maps to a label

OptionLabelFromValue(func(ctx FieldContext, value string) (string, error) {
    var user models.User
    database.DB.Find(&user, value)
    return user.Name, nil
}).

In the example above, you can see we map a user's ID to a user's name. Therefore the options for your dropdown have a key of User Ids, and labels of User Names.

Remote Search Debounce

You can specify the search debounce when performing a remote search as so:

go
// static
FormSelect("name").
    RemoteSearchDebounceMs(1000)
    
// callback
FormSelect("name").
    RemoteSearchDebounceMsFn(
        func (ctx FieldContext) uint {
            return 235
        }
    )

Native

By default, Iridium renders a custom, styled, dropdown as part of your views.

If you'd rather render your browser's native dropdown, you can do so as follows:

go
// static
FormSelect("cities").
    Native()


// callback
FormSelect("cities").
    NativeFn(
        func (ctx FieldContext) bool {
            return false
        }
    )

Released under the MIT License.