Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
---
2
layout: docs
3
title: "Dropdowns"
4
description: "A reusable dropdown component"
5
date: 2023-06-13T10:10:00+08:00
6
draft: false
7
tags:
8
- MDL-78279
9
- "4.3"
10
---
11
 
12
## How it works
13
 
14
Moodle dropdowns are output components to generate elements that expand extra floating information when clicked.
15
 
16
Currently, the core comes with two prebuild dropdowns:
17
 
18
- Dropdown dialog: to display rich content inside the dropdown area.
19
- Dropdown status: to display a list of available statuses
20
 
21
## Source files
22
 
23
- `lib/classes/output/local/dialog.php`: to define a dropdown dialog.
24
- `lib/classes/output/local/dialog.php`: to define a dropdown dialog.
25
- `lib/classes/output/choicelist.php`: generic output class to define a user choice.
26
- `lib/templates/local/dropdown/dialog.mustache`
27
- `lib/templates/local/dropdown/status.mustache`
28
 
29
## Usage
30
 
31
### Dropdown dialog
32
 
33
The constructor for the dropdown dialog class only requires three parameters.
34
 
35
- The button content
36
- The dropdown content
37
- An array of additional definitions. However, the output public methods can override all the definition values once the instance is created. Check out the examples to learn how to use them
38
 
39
The following example is the most simple example of creating a dropdown:
40
 
41
{{< php >}}
42
$dialog = new core\output\local\dropdown\dialog('Open dialog button', 'Dialog content');
43
echo $OUTPUT->render($dialog);
44
{{< / php >}}
45
 
46
You have the option to include additional classes to the main component but also to the button itself.
47
 
48
{{< php >}}
49
$dialog = new core\output\local\dropdown\dialog(
50
    'Open dialog',
51
    'Dialog content',
52
    [
53
        'classes' => 'mb-4',
54
        'buttonclasses' => 'btn btn-primary extraclass',
55
    ]
56
);
57
echo $OUTPUT->render($dialog);
58
{{< / php >}}
59
 
60
{{< mustache template="core/local/dropdown/dialog" >}}
61
    {
62
        "buttonid" : "example01",
63
        "buttoncontent" : "Open dialog",
64
        "dialogcontent" : "Dialog content",
65
        "buttonclasses": "btn btn-primary extraclass"
66
    }
67
{{< /mustache >}}
68
 
69
If a specific item is floating towards the end of the page, you might consider aligning the dropdown menu to the left rather than to the right. To achieve this, you can use the POSITION constant values to set the `dropdownposition` $definition attribute or input it into the `set_position` method.
70
 
71
{{< php >}}
72
$dialog = new core\output\local\dropdown\dialog('Open dialog', 'Dialog content');
73
$dialog->set_position(core\output\local\dropdown\dialog::POSITION['end']);
74
echo $OUTPUT->render($dialog);
75
{{< / php >}}
76
 
77
By default, the dropdown width will adapt to the content. However, for long texts, there may be better scenarios. You can use the WIDTH constant values to set the `dialogwidth` $definition attribute or input it into the `set_dialog_width` method.
78
 
79
{{< php >}}
80
// Big but fixed-width example.
81
$dialog = new core\output\local\dropdown\dialog('Big dialog', $content);
82
$dialog->set_dialog_width(core\output\local\dropdown\dialog::WIDTH['big']);
83
echo $OUTPUT->render($dialog);
84
 
85
// Small width example.
86
$dialog = new core\output\local\dropdown\dialog('Small dialog', $content);
87
$dialog->set_dialog_width(core\output\local\dropdown\dialog::WIDTH['small']);
88
echo $OUTPUT->render($dialog);
89
{{< / php >}}
90
 
91
{{< mustache template="core/local/dropdown/dialog" >}}
92
    {
93
        "buttonid" : "example02",
94
        "buttoncontent" : "Big dialog",
95
        "dialogcontent" : "This is a long content for a big dialog that will be displayed in a fixed-width container.",
96
        "buttonclasses": "btn btn-primary extraclass",
97
        "dialogclasses": "dialog-big"
98
    }
99
{{< /mustache >}}
100
 
101
### Dropdown status
102
 
103
The dropdown status is a user-choice wrapper. To create it, first, you need to create an instance of `core\output\choicelist` that will be used to generate the dropdown content data.
104
 
105
{{< php >}}
106
$choice = new core\output\choicelist('Dialog content');
107
$choice->add_option('option1', 'Option 1');
108
$choice->add_option('option2', 'Option 2');
109
$choice->add_option('option3', 'Option 3');
110
$choice->set_selected_value('option2');
111
 
112
$dialog = new core\output\local\dropdown\status('Open dialog button', $choice);
113
echo $OUTPUT->render($dialog);
114
{{< / php >}}
115
 
116
{{< mustache template="core/local/dropdown/status" >}}
117
    {
118
        "buttonid" : "example04",
119
        "buttoncontent" : "Open dialog button",
120
        "dialogcontent" : "Dialog content",
121
        "choices" : {
122
            "hasoptions" : true,
123
            "options" : [
124
                {
125
                    "optionid" : "option1",
126
                    "value" : "option1",
127
                    "name" : "Option 1",
128
                    "hasicon" : false,
129
                    "first" : true,
130
                    "optionnumber" : 1,
131
                    "optionuniqid" : "option1uniqid"
132
                },
133
                {
134
                    "optionid" : "option2",
135
                    "value" : "option2",
136
                    "name" : "Option 2",
137
                    "hasicon" : false,
138
                    "selected" : true,
139
                    "optionnumber" : 2,
140
                    "optionuniqid" : "option2uniqid"
141
                },
142
                {
143
                    "optionid" : "option3",
144
                    "value" : "option3",
145
                    "name" : "Option 3",
146
                    "hasicon" : false,
147
                    "optionnumber" : 3,
148
                    "optionuniqid" : "option3uniqid"
149
                }
150
            ]
151
        }
152
    }
153
{{< /mustache >}}
154
 
155
The status dropdown is an extension of the dropdown dialog, which means that all the definitions mentioned earlier can also be applied to it.
156
 
157
- The status dropdown is also compatible with all the `core\output\choicelist` extra features like:
158
- Adding additional icons and descriptions to the options
159
- Disable options
160
- Add links to options
161
 
162
The following example shows how to use the advanced features:
163
 
164
{{< php >}}
165
$choice = new core\output\choicelist('Dialog content');
166
 
167
// Option one is a link.
168
$choice->add_option('option1', 'Option 1', [
169
    'url' => new moodle_url('/'),
170
]);
171
// Option two has an icon and description.
172
$choice->add_option('option2', 'Option 2', [
173
    'description' => 'Option 2 description',
174
    'icon' => new pix_icon('t/hide', 'Eye icon 2')
175
]);
176
// Option three is disabled.
177
$choice->add_option('option3', 'Option 3', [
178
    'disabled' => true,
179
]);
180
 
181
$choice->set_selected_value('option2');
182
 
183
$dialog = new core\output\local\dropdown\status('Open dialog button', $choice);
184
echo $OUTPUT->render($dialog);
185
{{< / php >}}
186
 
187
#### Sync button text with selected status
188
 
189
The status dropdown can be configured to sync the button text with the selected status.
190
 
191
To do so, you need to set the `buttonsync` $definition attribute to `true`.
192
 
193
{{< php >}}
194
$choice = new core\output\choicelist();
195
$choice->add_option('option1', get_string('option1', YOURPLUGIN));
196
$choice->add_option('option2', get_string('option2', YOURPLUGIN));
197
$choice->set_selected_value('option2');
198
 
199
// Add some attribute to select through a query selector.
200
$dialog = new core\output\local\dropdown\status(
201
    get_string('buttontext', YOURPLUGIN),
202
    $choice,
203
    [
204
        'extras' => ['id' => 'mydropdown'],
205
        'buttonsync' => true,
206
        // With 'updatestatus' it will change the status when the user clicks an option
207
        // See "Dropdown status in update mode" section for more information.
208
        'updatestatus' => true,
209
    ]
210
);
211
echo $OUTPUT->render($dialog);
212
{{< / php >}}
213
 
214
## Javascript
215
 
216
### Controlling dropdowns
217
 
218
Both `core/local/dropdown/status` and `core/local/dropdown/status` AMD modules provide functions to:
219
 
220
- Open and close the dropdown.
221
- Change the button content.
222
- Get the main dropdown HTML element.
223
 
224
Both modules are object-oriented. To get the dropdown instance, the process is as follows:
225
 
226
1. Add id or data attributes to the main component to select it using a query selector.
227
2. Import `getDropdownDialog` from `core/local/dropdown/dialog`, or `getDropdownStatus` from `core/local/dropdown/status`, depending on whether you use a dialogue or a status dropdown.
228
3. Call `getDropdownDialog` or `getDropdownStatus` with the query selector to get the instance
229
 
230
Both classes provide the following methods:
231
 
232
- `setVisible(Boolean)` to open or close the dropdown.
233
- `isVisible()` to know if it is open or closed.
234
- `setButtonContent(String)` to replace the button content.
235
- `setButtonDisabled(Boolean)` to disable or enable the dropdown button.
236
- `getElement()`to get the main HTMLElement to add eventListeners.
237
 
238
The following example uses the module to open the dropdown when an extra button is preset:
239
 
240
```js
241
import {getDropdownDialog} from 'core/local/dropdown/';
242
 
243
const dialog = getDropdownDialog('[MYDROPDOWNSELECTOR]');
244
document.querySelector('[data-for="openDropdown"]').addEventListener('click', (event) => {
245
    event.stopPropagation();
246
    dialog.setVisible(true);
247
});
248
```
249
 
250
### Specific dropdown status methods
251
 
252
The `core/local/dropdown/status` provides extra controls for the status selector, such as:
253
 
254
- `getSelectedValue()` and `setSelectedValue(String)` to control the currently selected status.
255
- `isButtonSyncEnabled()` and `setButtonSyncEnabled(Boolean)` to synchronise the button text with the selected status.
256
- `isUpdateStatusEnabled()` and `setUpdateStatusEnabled(Boolean)` to control the auto-update status mode.
257
 
258
## Using dropdown status from the frontend
259
 
260
The dropdown status can operate in two different ways.
261
 
262
### Dropdown status in display only
263
 
264
The display-only is the default behaviour for any dropdown. In display-only mode, the component will show all the status values to the user, but it won't handle and click the event nor change the current status.
265
 
266
If a plugin wants to change the status value when the user clicks, it should  code a custom module to:
267
 
268
1. Capture `click` event listeners to the choice items.
269
2. Send the new status to the backend (using an ad-hoc webservice).
270
3. If the webservice execution is ok, update the component value using the `setSelectedValue` instance method.
271
 
272
The following example shows how to render a display-only dropdown status in the backend:
273
 
274
{{< php >}}
275
$choice = new core\output\choicelist('Dialog content');
276
 
277
// Add some data attributes to the choices.
278
$choice->add_option(
279
    'option1',
280
    get_string('option1', YOURPLUGIN), [
281
    extras' => ['data-action' => 'updateActionName']
282
]);
283
$choice->add_option(
284
    'option2',
285
    get_string('option2', YOURPLUGIN), [
286
    extras' => ['data-action' => 'updateActionName']
287
]);
288
$choice->set_selected_value('option2');
289
 
290
// Add some attribute to select through a query selector.
291
$dialog = new core\output\local\dropdown\status(
292
    get_string('buttontext', YOURPLUGIN),
293
    $choice,
294
    ['extras' => ['id' => 'mydropdown']]
295
);
296
echo $OUTPUT->render($dialog);
297
{{< / php >}}
298
 
299
Having this PHP code, the AMD controller could be something like:
300
 
301
```js
302
import {getDropdownStatus} from 'core/local/dropdown/status';
303
import {sendValueToTheBackend} from 'YOURPLUGIN/example';
304
 
305
const status = getDropdownStatus('#mydropdown');
306
status.getElement().addEventListener('click', (event) => {
307
    const option = event.target.closest("[data-action='updateActionName']");
308
    if (!option) {
309
        return;
310
    }
311
    try {
312
        if(sendValueToTheBackend(option.dataset.value)) {
313
             status.setSelectedValue(option.dataset.value);
314
        }
315
    } catch (error) {
316
        // Do some error handling here.
317
    }
318
});
319
```
320
 
321
### Dropdown status in update mode
322
 
323
The component will act more like an HTML radio button in update mode. It will store the current status value and will trigger `change` events when the value changes.
324
 
325
In this case, the plugin controller has to:
326
 
327
1. Capture the component element `change` event. Remember that, as in radio events, the `change` event won't bubble, so it cannot be delegated to a parent element.
328
2. Send the new status to the backend (using an ad-hoc webservice).
329
3. If the webservice execution fails, do a value rollback using the `setSelectedValue` instance method.
330
 
331
The following example shows how to render an update mode dropdown status in the backend:
332
 
333
{{< php >}}
334
$choice = new core\output\choicelist('Dialog content');
335
 
336
$choice->add_option('option1', get_string('option1', YOURPLUGIN));
337
$choice->add_option('option2', get_string('option2', YOURPLUGIN));
338
$choice->set_selected_value('option2');
339
 
340
// Add some attribute to select through a query selector.
341
$dialog = new core\output\local\dropdown\status(
342
    get_string('buttontext', YOURPLUGIN),
343
    $choice,
344
    [
345
        'extras' => ['id' => 'mydropdown'],
346
        'updatestatus' => true,
347
    ]
348
);
349
echo $OUTPUT->render($dialog);
350
{{< / php >}}
351
 
352
Having this PHP code, the AMD controller could be something like:
353
 
354
```js
355
import {getDropdownStatus} from 'core/local/dropdown/status';
356
import {sendValueToTheBackend} from 'YOURPLUGIN/example';
357
 
358
const status = getDropdownStatus('#mydropdown');
359
let currentValue = status.getSelectedValue();
360
 
361
status.getElement().addEventListener('change', (event) => {
362
    if (currentValue == status.getSelectedValue()) {
363
        return;
364
    }
365
    try {
366
        sendValueToTheBackend(status.getSelectedValue());
367
        currentValue = status.getSelectedValue();
368
    } catch (error) {
369
        status.setSelectedValue(currentValue);
370
    }
371
});
372
```
373
 
374
**Note**: the `event.target` is also the main element. You can also get the current value from `event.target.dataset.value` if you prefer.
375
 
376
## Examples
377
 
378
<!-- markdownlint-disable-next-line MD033 -->
379
<iframe src="../../../../examples/dropdowns.php" style="overflow:hidden;height:400px;width:100%;border:0" title="Moodle dynamic tabs"></iframe>