Статьи

Grails chained select — загружать данные в одном выпадающем списке в зависимости от другого

Из этого туториала вы узнаете, как создать два поля выбора, в которых на элементы второго блока выбора влияет значение первого. Разработчики сталкиваются с этим обычно, и это также называется цепным выбором. Примером является выбор страны с последующим выбором штата. Выбор штатов должен основываться на том, какая страна была выбрана.

Пример вывода

Вот пример вывода. У нас есть поле выбора для категории с последующим выбором для подкатегории. Первоначально, когда ни один элемент не выбран в первом поле, второй является пустым.

AA01

Когда пользователь выбирает категорию цвета , подкатегории будут отображать различные цвета.

aa02

Когда пользователь выбирает категорию « Форма» , подкатегории будут отображать различные формы выбора.

aa03

Тестовый класс домена

В этом примере есть два класса домена.

1
2
3
4
5
6
7
8
package asia.grails.test
class Category {
    static hasMany = [subCategories:SubCategory]
    String name
    public String toString() {
        return name
    }
}
1
2
3
4
5
6
7
8
9
package asia.grails.test
class SubCategory {
    static belongsTo = Category
    Category category
    String name
    public String toString() {
        return name
    }
}

Это просто отношения один ко многим.

Тестовые данные

Заполняем тестовые данные через Bootstrap.groovy

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
import asia.grails.test.Category
import asia.grails.test.SubCategory
class BootStrap {
    def init = { servletContext ->
        if ( Category.count() == 0 ) {
            Category color = new Category(name:'Color').save()
            new SubCategory(category:color, name:'Red').save()
            new SubCategory(category:color, name:'Green').save()
            new SubCategory(category:color, name:'Blue').save()
            Category shape = new Category(name:'Shape').save()
            new SubCategory(category:shape, name:'Square').save()
            new SubCategory(category:shape, name:'Circle').save()
            Category size = new Category(name:'Size').save()
            new SubCategory(category:size, name:'Small').save()
            new SubCategory(category:size, name:'Medium').save()
            new SubCategory(category:size, name:'Large').save()
        }
    }
    def destroy = {
    }
}

У нас будет 3 категории: цвет, форма и размер.

Цепная форма выбора

Мы показываем форму. Код контроллера прост:

1
2
3
4
5
package asia.grails.test
class TestController {
    def form() {
    }
}

Содержимое form.gsp это.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<%@ page import="asia.grails.test.Category" %>
<!DOCTYPE html>
<html>
    <head>
        <meta name="layout" content="main">
        <title>Chained Select Test</title>
         <g:javascript library='jquery' />
    </head>
    <body>
        <div>
            <b>Category: </b>
            <g:select id="category" name="category.id" from="${Category.listOrderByName()}" optionKey="id"
                      noSelection="[null:' ']"
                      onchange="categoryChanged(this.value);" />
        </div>
        <div>
            <b>Sub-Category: </b>
            <span id="subContainer"></span>
        </div>
        <script>
            function categoryChanged(categoryId) {
                <g:remoteFunction controller="test" action="categoryChanged"
                    update="subContainer"
                    params="'categoryId='+categoryId"/>
            }
        </script>
    </body>
</html>

Второе поле выбора отображается внутри диапазона с id = subContainer . Он обновляется при каждом изменении категории ( onchange = ”categoryChanged (this.value); ). Метод для отображения подкатегорий — через вызов AJAX. Тег remoteFunction может использоваться для асинхронного вызова действия контроллера.

Также важно включить библиотеку JQuery. Это делается с помощью этого кода GSP:

1
<g:javascript library='jquery' />

Начиная с Grails 2.4, теги Grails AJAX устарели. Альтернативой является ручное кодирование метода javascript. Вот альтернативная реализация функции categoryChanged.

1
2
3
4
5
<script>
    function categoryChanged(categoryId) {
        jQuery.ajax({type:'POST',data:'categoryId='+categoryId, url:'/forum/test/categoryChanged',success:function(data,textStatus){jQuery('#subContainer').html(data);},error:function(XMLHttpRequest,textStatus,errorThrown){}});
    }
</script>

Это длиннее, но все же интуитивно понятно.

Вот остальная часть кода контроллера, который включает метод, который будет отображать второе поле выбора.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
package asia.grails.test
class TestController {
    def form() {
    }
    def categoryChanged(long categoryId) {
        Category category = Category.get(categoryId)
        def subCategories = []
        if ( category != null ) {
            subCategories = SubCategory.findAllByCategory(category, [order:'name'])
        }
        render g.select(id:'subCategory', name:'subCategory.id',
            from:subCategories, optionKey:'id', noSelection:[null:' ']
        )
    }
}

Метод categoryChanged контроллера вызывается функцией AJAX. Он возвращает HTML-код блока выбора и визуализирует его внутри span с id = subContainer .