Это должно было быть сравнительно простой задачей: переписать простой элемент управления, устранить некоторые известные ошибки и подготовить его для Moodle 3.2.

Выбор курса

В рамках нашего инструмента «StrathCom», который позволяет сотрудникам создавать уведомления, ориентированные на их классы, мы написали несколько лет назад Инструмент выбора курса.

Это позволяет пользователю выбирать из списка своих классов список «целей», на которых будет сосредоточено их внимание. Это может быть курс, группа или группировка, или комбинации вышеперечисленного.

AMD

Итак, первой задачей было убрать реализацию YUI и преобразовать ее в модуль AMD, а также использовать доступность jQuery в Moodle.

Наша предыдущая итерация использовала элемент управления деревом YUI, который сейчас устарел. К счастью, Moodle представила модуль ядра/дерева AMD.

К сожалению, это не очень хорошо задокументировано…

Есть 2 способа использования дерева, один — просто использовать вложенные LI, что, по вашему мнению, должно быть очевидным, однако все реализации Moodle (все 2) не следуют этому шаблону. Вместо этого они используют шаблон, который включает определение элементов с атрибутами ara-*.

Я начал с выгрузки всех статических аспектов в шаблон .mustache. Поскольку я знал, что у нас будет список курсов (т.е. уровень 1), когда страница загружается, имело смысл предоставить некоторый контент хорошо и заранее, оставшиеся узлы будут отображаться на уровне 3 (с уровнем 2, являющимся контейнером, позволяющим пользователю различать группы и группировки).

<div id=’{{instanceid}}_search’ class=”local_strathcourseselect search_container”>
<input id=”{{instanceid}}_searchbox” 
 name=”{{instanceid}}_searchbox” class=”local_strathcourseselect search_input”
 type=”text” placeholder=”Find a course”/>
</div>
<div id=”{{instanceid}}_treecontainer” class=”local_strathcourseselect treecontainer”>
 <ul id=”{{instanceid}}_tree” role=”tree” 
 data-ajax-loader=”local_strathcourseselect/courseselectorloader” 
 class=”local_strathcourseselect tree”>
 {{#courses}}
 <li class=”contains_branch”>
 <p role=”treeitem” class=”branch tree_item course_item” aria-owns=”group_{{id}}” aria-expanded=”false”
 data-course-shortname=”{{shortname}}{{^shortname}}Unnamed Course ID: {{id}}{{/shortname}}”
 >
 <input type=”checkbox” name=”course_{{id}}” data-node-key=”{{id}}”
 data-node-type=”course” 
 class=”local_strathcourseselector itemselector”>
 {{shortname}}{{^shortname}}Unnamed Course ID: {{id}}{{/shortname}}
 </p>
 <ul role=”group” aria-hidden=”true” id=”group_{{id}}”>
 <li class=”contains_branch”>
 <p role=”treeitem” class=”branch tree_item” 
 aria-expanded=”false”
 data-requires-ajax=”true”
 data-loaded=”false”
 data-node-id=”group_{{id}}”
 data-node-key=’{{id}}’
 data-node-type=’groups’
 >Groups</p>
 </li>
 <li class=”contains_branch”>
 <p role=”treeitem” class=”branch tree_item” 
 aria-expanded=”false”
 data-requires-ajax=”true”
 data-loaded=”false”
 data-node-id=”grouping_{{id}}”
 data-node-key=’{{id}}’
 data-node-type=’groupings’
 >Groupings</p>
 </li>
 
 </ul>
 </li>
 {{/courses}}
 </ul>
</div>

Этот шаблон в основном устанавливает контейнер *_treecontainer, который содержит верхний уровень ul, содержащий все узлы курса, которые может видеть пользователь.

Каждый узел курса представлен узлом li, состоящим из узла p, а затем узла ul, содержащего 2 узла li (это узлы группы и группировки «заголовок»).

Это может показаться обратным, но сначала мы рассмотрим «дочерние» узлы. Дочернему ul должен быть присвоен role=”group”, чтобы обозначить, что он расширяемый, а также должен быть украшен уникальным тегом id. В этом случае (что несколько сбивает с толку) все они были названы «X_Y», где X указывает, является ли это курсом, а Y — это внутренний идентификатор Moodle соответствующего курса.

Каждая группа уровня курса содержит два li, которые будут крюком, к которому будут прикреплены группы и группы курса. Однако они будут загружены AJAX позже.

Поскольку древовидная структура фактически будет представлена ​​тегами P, каждый из них аннотирован атрибутом role=”treeitem». Чтобы связать метку с элементами, которые она может расширять, атрибут aria-owns устанавливается, чтобы указать, какое ulлюбое взаимодействие будет затронуто.

Создание экземпляра дерева

Согласно документам Moodle, это должно быть так же просто, как передать селектор jQuery компоненту дерева:

var tree = new Tree('#treecontainer');

Все должно встать на свои места…

Загрузка узлов с помощью AJAX

Прикрепив data-ajax-loader, вы можете указать дерево модуля AMD, которое можно использовать для динамической загрузки данных. В этом случае я создал новый модуль AMD с именем courseselectorload и вставил в него тот же модуль.

Этот модуль должен реализовать метод load(element).

define(['jquery', 'core/ajax', 'core/config', 'local_strathcourseselect/ajax_response_renderer'],
    function($, ajax, config, renderer) {
        var URL = config.wwwroot+'/local/strathcourseselect/ajax.php';
        window.console.log(URL);
        return {
            load: function(element) {
                element = $(element);
                console.log(element);
                var promise = $.Deferred();
                var data = {
                    elementid: element.attr('data-node-id'),    // this is the ID of the node taht requested the data
                    id: element.attr('data-node-key'),          // this is what course id
                    type: element.attr('data-node-type'),       // This is the type of data to retrieve about the course (groups / groupings)
                    sesskey: config.sesskey
                };
                var settings = {
                    type: "POST",
                    dataType: "json",
                    data:data
                };
                window.console.log(settings);
                $.ajax(URL, settings).done(function(nodes) {
                    window.console.log("Got Data");
                    window.console.log(nodes);
                    if (!nodes) {
                        renderer.renderNoItems(element);
                    } else {
                        renderer.render(element, nodes);
                    }
                    promise.resolve();
                });
                
                return promise;
            }
        }
    }
);

Эти модули отвечают за извлечение данных, а затем выполнение любых действий, необходимых для их добавления в DOM.

Однако я последовал примеру Moodle и перенес все манипуляции с DOM в отдельный класс ajax_response_renderer, который все это обрабатывает.

Затем украсьте li items, представляющие узлы, которые будут расширены, с помощью data-requires-aja="true"атрибута, в данном случае ul, который содержит заголовки Groups и Groupings:

<ul role="group" aria-hidden="true" id="group_{{id}}">
                    <li class="contains_branch">
                        <p role="treeitem" class="branch tree_item"  
                        aria-expanded="false"
                        data-requires-ajax="true"
                        data-loaded="false"
                        data-node-id="group_{{id}}"
                        data-node-key='{{id}}'
                        data-node-type='groups'
                        >Groups</p>
                    </li>
                    <li class="contains_branch">
                        <p role="treeitem" class="branch tree_item"  
                            aria-expanded="false"
                            data-requires-ajax="true"
                            data-loaded="false"
                            data-node-id="grouping_{{id}}"
                            data-node-key='{{id}}'
                            data-node-type='groupings'
                            >Groupings</p>
                    </li>
    
                </ul>

В этом случае я использовал data-node-type для представления типа предметов, которые мне нужны, и атрибут data-node-key для хранения идентификатора курса, для которого я хочу его использовать. data-node-id содержит идентификатор содержащей группы.

Теперь, когда пользователь расширяет узел курса верхнего уровня, он получит еще 2 опции «Группы» и «Группировки». Затем, когда они расширят один из них, элемент управления деревом попытается использовать загрузчик данных для извлечения новых элементов, передав идентификатор курса и тип узлов, которые мы хотим, на страницу, которая возвращает соответствующие данные.

Теоретически у вас есть элемент управления деревом, который загружает данные с помощью AJAX.

к несчастью

Это история о «к сожалению», наша предыдущая реализация не отображала дерево в строке, оно отображалось в модальном окне. Так что мне пришлось потрудиться, чтобы сделать это, и снова Moodle приходит на помощь с компонентом core/modal… который не задокументирован (пока).

Справедливости ради стоит сказать, что попытка обернуть дерево модальным компонентом имела свои собственные трудности, в том числе склонность Moodle делать все по шаблонам.

В итоге мы смогли заставить его работать:

  1. скрипт рендерит начальную древовидную структуру статически по усам,
  2. создайте экземпляр модального компонента, используя ModalFactory (НЕ ПЫТАЙТЕСЬ СДЕЛАТЬ ЭТО, ИСПОЛЬЗУЯ MODAL НАПРЯМУЮ, ЭТО ВЕДЕТ ВАС!!!!)
  3. прикрепите исходную древовидную структуру HTML к модальному атрибуту body
  4. Как только модальное обещание разрешается
  5. прикрепить модальное окно к DOM()
  6. Создайте экземпляр управления деревом и все остальное.

Важно не забыть сделать шаг 5! Модальное окно сделает это автоматически, когда вы сделаете его видимым, но пока оно не будет прикреплено, вы не сможете прикрепить какие-либо обработчики к чему-либо в теле.