Skip to content

Commit 3504e0b

Browse files
author
“Filippo”
committed
[docs] inline edit
1 parent 5664a7e commit 3504e0b

File tree

8 files changed

+135
-9
lines changed

8 files changed

+135
-9
lines changed

data/migratedPages.yml

+5-2
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,9 @@ Groups_API:
249249
Hardening_new_Roles_system:
250250
- filePath: "/docs/apis/subsystems/roles.md"
251251
slug: "/docs/apis/subsystems/roles"
252+
Inplace_editable:
253+
- filePath: "/docs/apis/subsystems/output/inplace.md"
254+
slug: "/docs/apis/subsystems/output/inplace"
252255
Integration_Review:
253256
- filePath: "/general/development/process/integration/index.md"
254257
slug: "/general/development/process/integration"
@@ -1477,10 +1480,10 @@ New_docs_version_process:
14771480
- filePath: "/general/development/process/release/newuserdocs.md"
14781481
slug: "/general/development/process/release/newuserdocs"
14791482
Output_API:
1480-
- filePath: "/docs/apis/subsystems/output.md"
1483+
- filePath: "/docs/apis/subsystems/output/index.md"
14811484
slug: "/docs/apis/subsystems/output"
14821485
Output_functions:
1483-
- filePath: "/docs/apis/subsystems/output.md"
1486+
- filePath: "/docs/apis/subsystems/output/index.md"
14841487
slug: "/docs/apis/subsystems/output#output-functions"
14851488
Overview:
14861489
- filePath: "/general/community/intro.md"

docs/apis.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ The [Page API](https://docs.moodle.org/dev/Page_API) is used to set up the curre
4040

4141
### Output API (output)
4242

43-
The [Output API](./apis/subsystems/output.md) is used to render the HTML for all parts of the page.
43+
The [Output API](./apis/subsystems/output/index.md) is used to render the HTML for all parts of the page.
4444

4545
### String API (string)
4646

Loading

docs/apis/subsystems/output.md renamed to docs/apis/subsystems/output/index.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ The template used in this plugin is located in the plugin's templates folder. Th
180180
</div>
181181
```
182182

183-
This is the mustache template for this demo. It uses some bootstrap classes directly to position and style the content on the page. `{{sometext}}` is replaced with the variable from the context when this template is rendered. For more information on templates see [Templates](../../guides/templates/index.md).
183+
This is the mustache template for this demo. It uses some bootstrap classes directly to position and style the content on the page. `{{sometext}}` is replaced with the variable from the context when this template is rendered. For more information on templates see [Templates](../../../guides/templates/index.md).
184184

185185
## Output Functions
186186

@@ -279,7 +279,7 @@ In earlier versions of Moodle, the third argument was integer `$courseid`. It is
279279
Those methods are designed to replace the old ```html_writer::tag(...)``` methods. Even if many of them are just wrappers around the old methods, they are more semantic and could be overridden by component renderers.
280280
:::
281281

282-
While to render complex elements, you should use [templates](../../guides/templates/index.md), some simple elements can be rendered using the following functions:
282+
While to render complex elements, you should use [templates](../../../guides/templates/index.md), some simple elements can be rendered using the following functions:
283283

284284
#### container()
285285

@@ -338,4 +338,4 @@ In the standard Boost theme this method will output a span using the [Bootstrap
338338
- [HTML Guidelines](https://docs.moodle.org/dev/HTML_Guidelines)
339339
- [Output renderers](https://docs.moodle.org/dev/Output_renderers)
340340
- [Overriding a renderer](https://docs.moodle.org/dev/Overriding_a_renderer)
341-
- [Templates](../../guides/templates/index.md)
341+
- [Templates](../../../guides/templates/index.md)
+123
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
---
2+
title: Inplace editable
3+
tags:
4+
- AJAX
5+
- Javascript
6+
---
7+
import { Since } from '@site/src/components';
8+
9+
<Since versions={["3.1"]} />
10+
11+
inplace_editable is a mini-API introduced under [MDL-51802](https://tracker.moodle.org/browse/MDL-51802) for Moodle 3.1. It allows developers easily add in-place editing of a value on any page. The interface is the same as sections and activity name editing. It is implemented as AMD module using JQuery and is re-usable.
12+
13+
![inplace editable example.png](./_inplace/inplace_editable_example.png)
14+
15+
## Implementing inplace_editable in a plugin
16+
17+
The best way is to explain the usage on a simple example. Imagine we have plugin `tool_mytest` that needs to implement in-place editing of a field 'name' from db table `tool_mytest_mytable`. We are going to call this itemtype "mytestname". Each plugin (or core component) may use as many itemtypes as it needs.
18+
19+
Define a callback in `/admin/tool/mytest/lib.php` that starts with the plugin name and ends with `_inplace_editable`:
20+
21+
```php
22+
function tool_mytest_inplace_editable($itemtype, $itemid, $newvalue) {
23+
if ($itemtype === 'mytestname') {
24+
global $DB;
25+
$record = $DB->get_record('tool_mytest_mytable', array('id' => $itemid), '*', MUST_EXIST);
26+
// Must call validate_context for either system, or course or course module context.
27+
// This will both check access and set current context.
28+
\external_api::validate_context(context_system::instance());
29+
// Check permission of the user to update this item.
30+
require_capability('tool/mytest:update', context_system::instance());
31+
// Clean input and update the record.
32+
$newvalue = clean_param($newvalue, PARAM_NOTAGS);
33+
$DB->update_record('tool_mytest_mytable', array('id' => $itemid, 'name' => $newvalue));
34+
// Prepare the element for the output:
35+
$record->name = $newvalue;
36+
return new \core\output\inplace_editable('tool_mytest', 'mytestname', $record->id, true,
37+
format_string($record->name), $record->name, 'Edit mytest name', 'New value for ' . format_string($record->name));
38+
}
39+
}
40+
```
41+
42+
In your renderer or wherever you actually display the name, use the same `inplace_editable` template:
43+
44+
```php
45+
$tmpl = new \core\output\inplace_editable('tool_mytest', 'mytestname', $record->id,
46+
has_capability('tool/mytest:update', context_system::instance()),
47+
format_string($record->name), $record->name, 'Edit mytest name', 'New value for ' . format_string($record->name));
48+
echo $OUTPUT->render($tmpl);
49+
```
50+
51+
This was a very simplified example, in the real life you will probably want to:
52+
53+
- Create a function (or class extending `core\output\inplace_editable`) to form the instance of templateable object so you don't need to duplicate code;
54+
- Use language strings for `edithint` and `editlabel`, best practice is to use `new lang_string` because these strings will not be needed if `editable=false`
55+
- Use an existing function to update a record (which hopefully also validates input value and triggers events)
56+
- Add unit tests and behat tests. There is a useful behat step **I press key "13" in the field "New value for myname"**
57+
58+
## Toggles and dropdowns
59+
60+
You may choose to set the UI for your inplace editable element to be a string value (default), toggle or dropdown.
61+
62+
Examples of dropdown setup (see also [example by overriding class](https://github.com/moodle/moodle/blob/master/tag/classes/output/tagareacollection.php)):
63+
64+
```php
65+
$tagcollections = \core_tag_collection::get_collections_menu(true);
66+
$tmpl = new \core\output\inplace_editable('core_tag', 'tagareacollection', $tagarea->id, $editable,
67+
null, $value, $edithint, $editlabel);
68+
$tmpl->set_type_select($tagcollections);
69+
// Note that $displayvalue is not needed (null was passed in the example above) - it will be automatically taken from options.
70+
// $value in the example above must be an existing index from the $tagcollections array, otherwise exception will be thrown.
71+
```
72+
73+
Example of toggle setup (see also [example by overriding class](https://github.com/moodle/moodle/blob/master/tag/classes/output/tagareaenabled.php)):
74+
75+
```php
76+
$tmpl = new \core\output\inplace_editable('core_tag', 'tagflag', $tag->id, $editable, $displayvalue, $value, $hint);
77+
$tmpl->set_type_toggle(array(0, 1));
78+
// Note that $editlabel is not needed.
79+
// $value must be an existing element of the array passed to set_type_toggle(), otherwise exception will be thrown.
80+
// $displayvalue in toggles is usually an image, for example closed/open eye. It is easier to implement by
81+
// overriding the class. In this case $displayvalue can be generated from $value during exporting.
82+
```
83+
84+
## How does it work
85+
86+
`inplace_editable` consists of
87+
88+
- Templateable/renderable **class core\output\inplace_editable**
89+
- Template **core/inplace_editable**
90+
- JavaScript module **core/inplace_editable**
91+
- Web service **core_update_inplace_editable** available from AJAX
92+
93+
All four call each other so it's hard to decide where we start explaining this circle of friends but let's start with web service.
94+
95+
1. **Web service** receives arguments (`$component`, `$itemtype`, `$itemid`, `$newvalue`) - it searches for the inplace_editable callback in the component. Then web service calls this callback as `{component}_inplace_editable($itemtype, $itemid, $newvalue)`, this must return templateable element which is sent back to the web service caller. Web service requires user to be logged in. **Any other `capability/access` checks must be performed inside the callback.**
96+
97+
2. **Templateable element** contains such properties as component, `itemtype`, `itemid`, `displayvalue`, `value`, `editlabel` and `edithint`. When used in a **template** It only renders the displayvalue and the edit link (with `title=edithint`). All other properties are rendered as `data-xxx` attributes. Template also ensures that JavaScript module is loaded.
98+
99+
3. **JavaScript module** registers a listener to when the edit link is clicked and then it replaces the displayvalue with the text input box that allows to edit value. When user presses "Enter" the AJAX request is called to the web service and code from the component is executed. If web service throws an exception it is displayed for user as a popup.
100+
101+
## Events
102+
103+
Plugin page can listen to JQuery events that are triggered on successful update or when update failed. Example of the listeners (as inline JS code):
104+
105+
```php
106+
$PAGE->requires->js_amd_inline("
107+
require(['jquery'], function(\$) {
108+
$('body').on('updatefailed', '[data-inplaceeditable]', function(e) {
109+
var exception = e.exception; // The exception object returned by the callback.
110+
var newvalue = e.newvalue; // The value that user tried to udpated the element to.
111+
e.preventDefault(); // This will prevent default error dialogue.
112+
// Do your own error processing here.
113+
});
114+
$('body').on('updated', '[data-inplaceeditable]', function(e) {
115+
var ajaxreturn = e.ajaxreturn; // Everything that web service returned.
116+
var oldvalue = e.oldvalue; // Element value before editing (note, this is raw value and not display value).
117+
// Do your own stuff, for example update all other occurences of this element on the page.
118+
});
119+
});
120+
");
121+
```
122+
123+
## See also

general/releases/3.1.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ There are no security issues included in this release, please refer to [Moodle 3
200200

201201
### Smaller new things
202202

203-
- [MDL-51802](https://tracker.moodle.org/browse/MDL-51802) - Reusable element for inplace editing ([documentation](https://docs.moodle.org/dev/Inplace_editable))
203+
- [MDL-51802](https://tracker.moodle.org/browse/MDL-51802) - Reusable element for inplace editing ([documentation](/docs/apis/subsystems/output/inplace))
204204
- [MDL-30811](https://tracker.moodle.org/browse/MDL-30811) - Introduce notification stack to moodle sessions ([documentation](https://docs.moodle.org/dev/Notifications))
205205
- [MDL-52237](https://tracker.moodle.org/browse/MDL-52237) - Add a callback to inject nodes in the user profile navigation ([documentation](/docs/apis/core/navigation/#user-profile))
206206
- [MDL-51324](https://tracker.moodle.org/browse/MDL-51324) - New course chooser element for moodleforms ([documentation](https://docs.moodle.org/dev/lib/formslib.php_Form_Definition#autocomplete))

versioned_docs/version-4.1/apis.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ The [Page API](https://docs.moodle.org/dev/Page_API) is used to set up the curre
4040

4141
### Output API (output)
4242

43-
The [Output API](./apis/subsystems/output.md) is used to render the HTML for all parts of the page.
43+
The [Output API](./apis/subsystems/output/index.md) is used to render the HTML for all parts of the page.
4444

4545
### String API (string)
4646

versioned_docs/version-4.2/apis.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ The [Page API](https://docs.moodle.org/dev/Page_API) is used to set up the curre
4040

4141
### Output API (output)
4242

43-
The [Output API](./apis/subsystems/output.md) is used to render the HTML for all parts of the page.
43+
The [Output API](./apis/subsystems/output/index.md) is used to render the HTML for all parts of the page.
4444

4545
### String API (string)
4646

0 commit comments

Comments
 (0)