# select

## Purpose

Helper tag for creating HTML selects.

## Examples

    // create a select from a range
    <g:select name="user.age" from="${18..65}" value="${age}"
              noSelection="['':'-Choose your age-']"/>// use a no selection with a nullable Object property (use 'null' as key)
     <g:select id="type" name='type.id' value="${person?.type?.id}"
        noSelection="${['null':'Select One...']}"
        from='${PersonType.list()}'
        optionKey="id" optionValue="name"></g:select>// create select from a list of companies
    // note the 'optionKey' is set to the id of each company element
    <g:select name="user.company.id"
              from="${Company.list()}"
              value="${user?.company.id}"
              optionKey="id" />// create select with internationalized labels (this is useful for small static lists and the inList constraint)
    // expected properties in messages.properties:
    // book.category.M=Mystery
    // book.category.T=Thriller
    // book.category.F=Fantasy
    <g:select name="book.category" from="${['M', 'T', 'F']}" valueMessagePrefix="book.category" />

Example as a method call in GSP only:

    ${select(from:aList,value:aValue)}

## Description

Attributes

- `from` (required) - The list or range to select from
- `value` (optional) - The current selected value that evaluates equals() to true for one of the elements in the from list.
- `optionKey` (optional) - By default `value` attribute of each `<option>` element will be the result of a "toString()" call on each element. Setting this allows the value to be a bean property of each element in the list.
- `optionValue` (optional) - By default the body of each `<option>` element will be the result of a "toString()" call on each element in the "from" attribute list. Setting this allows the value to be a bean property of each element in the list.
- `keys` (optional) - A list of values to be used for the value attribute of each "option" element.
- `noSelection` (optional) - A single-entry map detailing the key and value to use for the "no selection made" choice in the select box. If there is no current selection this will be shown as it is first in the list, and if submitted with this selected, the key that you provide will be submitted. Typically this will be blank - but you can also use 'null' in the case that you're passing the ID of an object
- `valueMessagePrefix` (Optional) - By default the value "option" element will be the result of a "toString()" call on each element in the "from" attribute list. Setting this allows the value to be resolved from the I18n messages. The valueMessagePrefix will be suffixed with a dot ('.') and then the value attribute of the option to resolve the message. If the message could not be resolved, the value is presented.

The `optionKey` and `optionValue` attribute of the `<g:select>` tag deserve special mention as these allow you to control what is displayed to the user within the resulting `<select>` tag and also the value which is submitted in a form submission. The default behaviour is to call `toString()` on each element in the `from` attribute, but for example if you had a list of `Book` domain classes this may not be useful behaviour.

As an example the following `<g:select>` uses the `optionKey` attribute to resolve the `id` property of each `Book` as the value of the `value` attribute in each `<option>` tag. It also uses the `optionValue` attribute of `<g:select>` to display the `title` property of each `Book` to the user:

    <g:select optionKey="id" optionValue="title" name="book.title" from="${bookList}" />

If you require even more control over how each `<option>` element is presented to the user you can use a closure to apply a transformation within the `optionValue` attribute. As an example, the following code transforms each `Book` title to upper case:

    <g:select optionKey="id" optionValue="${{it.title?.toUpperCase()}}" name="book.title" from="${bookList}" />

## Source

Show Source

    def select = {attrs ->
            def messageSource = grailsAttributes.getApplicationContext().getBean("messageSource")
            def locale = RCU.getLocale(request)
            def writer = out
            attrs.id = attrs.id ? attrs.id : attrs.name
            def from = attrs.remove('from')
            def keys = attrs.remove('keys')
            def optionKey = attrs.remove('optionKey')
            def optionValue = attrs.remove('optionValue')
            def value = attrs.remove('value')
            if (value instanceof Collection && attrs.multiple == null) {
                attrs.multiple = 'multiple'
            }
            if (value instanceof StreamCharBuffer) {
            	value = value.toString()
            }
            def valueMessagePrefix = attrs.remove('valueMessagePrefix')
            def noSelection = attrs.remove('noSelection')
            if (noSelection != null) {
                noSelection = noSelection.entrySet().iterator().next()
            }
            def disabled = attrs.remove('disabled')
            if (disabled && Boolean.valueOf(disabled)) {
                attrs.disabled = 'disabled'
            }        writer << "<select name=\"${attrs.remove('name')?.encodeAsHTML()}\" "
            // process remaining attributes
            outputAttributes(attrs)        writer << '>'
            writer.println()        if (noSelection) {
                renderNoSelectionOptionImpl(writer, noSelection.key, noSelection.value, value)
                writer.println()
            }        // create options from list
            if (from) {
                from.eachWithIndex {el, i ->
                    def keyValue = null
                    writer << '<option '
                    if (keys) {
                        keyValue = keys[i]
                        writeValueAndCheckIfSelected(keyValue, value, writer)
                    }
                    else if (optionKey) {
                        def keyValueObject = null
                        if (optionKey instanceof Closure) {
                            keyValue = optionKey(el)
                        }
                        else if (el != null && optionKey == 'id' && grailsApplication.getArtefact(DomainClassArtefactHandler.TYPE, el.getClass().name)) {
                            keyValue = el.ident()
                            keyValueObject = el
                        }
                        else {
                            keyValue = el[optionKey]
                            keyValueObject = el
                        }
                        writeValueAndCheckIfSelected(keyValue, value, writer, keyValueObject)
                    }
                    else {
                        keyValue = el
                        writeValueAndCheckIfSelected(keyValue, value, writer)
                    }
                    writer << '>'
                    if (optionValue) {
                        if (optionValue instanceof Closure) {
                            writer << optionValue(el).toString().encodeAsHTML()
                        }
                        else {
                            writer << el[optionValue].toString().encodeAsHTML()
                        }
                    }
                    else if (valueMessagePrefix) {
                        def message = messageSource.getMessage("${valueMessagePrefix}.${keyValue}", null, null, locale)
                        if (message != null) {
                            writer << message.encodeAsHTML()
                        }
                        else if (keyValue) {
                            writer << keyValue.encodeAsHTML()
                        }
                        else {
                            def s = el.toString()
                            if (s) writer << s.encodeAsHTML()
                        }
                    }
                    else {
                        def s = el.toString()
                        if (s) writer << s.encodeAsHTML()
                    }
                    writer << '</option>'
                    writer.println()
                }
            }
            // close tag
            writer << '</select>'
        }    def typeConverter = new SimpleTypeConverter()
        private writeValueAndCheckIfSelected(keyValue, value, writer) {
            writeValueAndCheckIfSelected(keyValue, value, writer, null)
        }    private writeValueAndCheckIfSelected(keyValue, value, writer, el) {        boolean selected = false
            def keyClass = keyValue?.getClass()
            if (keyClass.isInstance(value)) {
                selected = (keyValue == value)
            }
            else if (value instanceof Collection) {
                // first try keyValue
                selected = value.contains(keyValue)
                if (! selected && el != null) {
                    selected = value.contains(el)
                }
            }
            else if (keyClass && value) {
                try {
                    value = typeConverter.convertIfNecessary(value, keyClass)
                    selected = (keyValue == value)
                } catch (Exception) {
                    // ignore
                }
            }
            writer << "value=\"${keyValue}\" "
            if (selected) {
                writer << 'selected="selected" '
            }
        }