From version < 66.3 >
edited by Adel Atallah
on 2019/01/10
To version < 67.1 >
edited by Marius Dumitru Florea
on 2019/06/21
< >
Change comment: There is no comment for this version

Summary

Details

Page properties
Author
... ... @@ -1,1 +1,1 @@
1 -XWiki.atallahade
1 +XWiki.mflorea
Content
... ... @@ -1,304 +9,550 @@
1 -{{velocity output="false"}}
2 -##
3 -## TODO:
4 -## - Document the REST usage of the suggest (this is the recommanded way of using the suggest now - when possible)
5 -## - Merge the example document in this one (why are there 2 documents ?)
6 -##
7 -{{/velocity}}
8 -
9 9  {{box cssClass="floatinginfobox" title="**Contents**"}}
10 -{{toc/}}
2 +{{toc start="2"/}}
11 11  {{/box}}
12 12  
13 -{{info}}
14 -This is a Javascript widget bundled by default with the XWiki platform.
15 -{{/info}}
5 +The suggest widget is implemented using [[Selectize.js>>http://selectize.github.io/selectize.js/]] with its Bootstrap skin and is bundled by default in XWiki platform.
16 16  
17 -= Usage =
7 +== Predefined Suggesters ==
18 18  
19 -The suggest widget can be triggered when typing something in a text field. The suggested list can contain field values from a class defined in your wiki, data retrieved from a custom document or a REST resource, or any custom information you provide.
9 +A couple of suggesters are available by default in XWiki. Each suggester provides:
20 20  
21 -== Suggest fields from a class defined in the wiki ==
11 +* dedicated helper Velocity macros (to import required resources and to output the required HTML)
12 +* a jQuery plugin to activate the suggest behavior on any input or select element
13 +* a CSS class that activates the suggest behavior automatically
22 22  
23 -Use information from a predefined class in your wiki (e.g. ##XWiki.TagClass##, ##XWiki.XWikiUsers##, etc.) or from a class defined by yourself.
15 +The dedicated Velocity macros are in fact loading a JavaScript code that calls the dedicated jQuery plugin on all the elements (input or select) that are marked with the dedicated CSS class. Here's the list of available suggesters:
24 24  
25 -For example, use **XWiki.TagClass** to suggest tags from the wiki tag cloud:
17 +|=Data|=Velocity Macros|=jQuery Plugin|=CSS Class
18 +|Pages|pagePicker, pagePicker_import|suggestPages|suggest-pages
19 +|Attachments|attachmentPicker, attachmentPicker_import|suggestAttachments|suggest-attachments
20 +|Users|userPicker, userPicker_input, userPicker_import|suggestUsers|suggest-users
21 +|Groups|groupPicker|suggestGroups|suggest-groups
22 +|Property Values|xpropertySuggestInputDisplayer|suggestPropertyValues|suggest-propertyValues
26 26  
27 -[[image:Doc[email protected]suggest.png]]
24 +=== Usage ===
28 28  
29 -{{code}}
30 -$!xwiki.jsx.use("DevGuide.AutoSuggestWidgetExample")
31 -<form method="post" action="#">
32 - <label for="myinput">Type the name of a tag and test the suggest list:</label>
33 - <input id="myinput" size="20" type="text" value=""/>
34 -</form>
26 +There are 3 ways in which you can activate a predefined suggester:
27 +
28 +* from Velocity (by calling the dedicated Velocity macro)
29 +* from JavaScript (by calling the dedicated jQyery plugin)
30 +* from HTML (by marking form fields with the dedicated CSS class)
31 +
32 +Use the one that suits you the best.
33 +
34 +==== From Velocity ====
35 +
36 +You can do this by putting the following code in the content of a wiki page. Here we're calling the dedicated ##pagePicker## Velocity macro. Note that you can pass configuration options. Most of these settings are translated into HTML attributes (of the generated suggest input element) read by the dedicated jQuery plugin. You can use the ##data-xwiki-selectize## parameter to pass configuration options to the [[Selectize.js>>https://github.com/selectize/selectize.js/blob/master/docs/usage.md#configuration]] widget. Each predefined suggester might also have custom parameters you can set, check their documentation below.
37 +
38 +{{code language="none"}}
39 +{{velocity}}
40 +{{html}}
41 +#set ($pagePickerParams = {
42 + 'name': 'targetPages',
43 + 'value': ['Path.To.Alice', 'Path.To.Bob'],
44 + 'multiple': 'multiple'
45 +})
46 +#pagePicker($pagePickerParams)
47 +{{/html}}
48 +{{/velocity}}
35 35  {{/code}}
36 36  
37 -The ##JavascriptExtension## object from the [[platform:DevGuide.AutoSuggestWidgetExample]] page contains the Javascript code to enable the widget when focusing on the text field:
51 +==== From JavaScript ====
38 38  
39 -{{code}}
40 -(function(){
41 - document.observe('dom:loaded', function () {
42 - if($('myinput')) {
43 - Event.observe($('myinput'), "focus", function() {
44 - new XWiki.widgets.Suggest(this, {
45 - script: '$xwiki.getURL("${doc.space}.WebHome", "view")?xpage=suggest&classname=XWiki.TagClass&fieldname=tags&secCol=-&',
46 - varname: "input",
47 - seps: " ,|",
48 - offsety: 13
49 - });
50 - });
51 - }
52 - }); // end of doc observe
53 -})();
53 +You can do this from a JavaScript Skin extension. First you need to configure the path to the suggester you want to use and then you need to call the dedicated jQuery plugin (##suggestAttachments##) from a require block. Note that you can pass configuration options when activating the suggester. These configuration options are either generic (for the [[Selectize>>https://github.com/selectize/selectize.js/blob/master/docs/usage.md#configuration]] widget) or specific to each suggester (see below).
54 +
55 +{{code language="js"}}
56 +require.config({
57 + paths: {
58 + 'xwiki-suggestAttachments': "$xwiki.getSkinFile('uicomponents/suggest/suggestAttachments.js', true)" +
59 + "?v=$escapetool.url($xwiki.version)"
60 + }
61 +});
62 +
63 +require(['jquery', 'xwiki-suggestAttachments'], function($) {
64 + $('input[name="logo"]').suggestAttachments({
65 + accept: 'image/,.mp4,.pdf',
66 + uploadAllowed: true
67 + });
68 +});
54 54  {{/code}}
55 55  
56 -=== Options used in the ##suggest.vm## template: ===
71 +Of course, you still need to load your skin extension code (from the content of a wiki page):
57 57  
58 -|=Option|=Details
59 -|xpage|For class properties use ##xpage=suggest## because the ##suggest.vm## template can handle such requests.
60 -|classname|The name of the class for the elements of the suggest list.
61 -|fieldname|The field name from the class considered for the suggest list.
62 -|firCol|First column of the list of results.
63 -|secCol|Second column of the list of results. For a user defined query, use **-** value for one column and no hidden input. Otherwise the list of results will have two columns and a hidden input.
73 +{{code language="none"}}
74 +{{velocity}}
75 +{{html}}
76 +#set ($discard = $xwiki.jsx.use('Path.To.Your.JSX'))
77 +<input type="text" name="logo" value="[email protected]"/>
78 +{{/html}}
79 +{{/velocity}}
80 +{{/code}}
64 64  
65 -=== Example ===
82 +==== From HTML ====
66 66  
67 -Check out the example for class field values at [[Class Field Example>>platform:DevGuide.AutoSuggestWidgetExample]]
84 +Put the following code in the content of a wiki page. First you need to import the resources required by the suggester and then you need to mark the form fields you wish to enhance (using the dedicated CSS class, ##suggest-users## here). Note that you can use the ##data-xwiki-selectize## attribute to pass configuration options in JSON format to the [[Selectize.js>>https://github.com/selectize/selectize.js/blob/master/docs/usage.md#configuration]] widget. Each predefined suggester might also have custom attributes you can set, check their documentation below.
68 68  
69 -== Suggest custom information ==
86 +{{code language="none"}}
87 +{{velocity output="false"}}
88 +#userPicker_import
89 +{{/velocity}}
70 70  
71 -When the information you want to suggest is not available through a class field or you generate it using a custom query, you need to create a service (plain wiki page called with the ##get## action and with ##outputSyntax=plain## parameter in the url, for example: ##xwiki/bin/get/Space/Page?outputSyntax=plain##) that maps your results to the **xml** input accepted by the widget. For example, you can build a list of suggestions that contains the wiki page names within a certain space:
91 +{{html}}
92 +<input type="text" name="manager" value="XWiki.mflorea" class="suggest-users"/>
93 +{{/html}}
94 +{{/code}}
72 72  
73 -[[image:Documentation.DevGuide.FrontendResources.AutoSuggestWidge[email protected]stomsuggest.png]]
96 +=== Suggest Pages ===
74 74  
75 -{{code}}
76 -$!xwiki.jsx.use("DevGuide.AjaxSuggestCustomExample")
77 -<form method="post" action="#">
78 - <label for="myinput">Type the name of an example page from the <tt>DevGuide</tt> space and test the suggest list:</label>
79 - <input id="myinput_suggest" size="20" type="text" value=""/>
80 - <input id="myinput" type="hidden" />
81 - <input id="mybutton" type="button" value="Go" /><br/>
82 -</form>
98 +This suggester displays the page rendered title and the page location. The value that is saved in the end is the page reference. The hidden page user preference is taken into account.
99 +
100 +{{code language="none"}}
101 +{{velocity}}
102 +{{html}}
103 +#set ($pagePickerParams = {
104 + ...
105 +})
106 +#pagePicker($pagePickerParams)
107 +{{/html}}
108 +{{/velocity}}
83 83  {{/code}}
84 84  
85 -The ##JavascriptExtension## object from the [[platform:DevGuide.AjaxSuggestCustomExample]] page contains the Javascript code to enable the widget when focusing on the text field. Also, the ##script## option uses the URL for the results page.
111 +{{image reference="pagePicker.png"/}}
86 86  
87 -{{code}}
88 -(function(){
89 - document.observe('dom:loaded', function () {
90 - if($('myinput_suggest')) {
91 - Event.observe($('myinput_suggest'), "focus", function() {
92 - new XWiki.widgets.Suggest(this, {
93 - script: "$xwiki.getURL('Documentation.DevGuide.FrontendResources.AutoSuggestWidget.SuggestService', 'get', 'outputSyntax=plain&spacename=Documentation.DevGuide')&",
94 - varname: "input",
95 - seps: " ,|",
96 - offsety: 13,
97 - minchars: 3
98 - });
99 - });
100 - }
101 - }); // end of doc observe
102 -})();
113 +Custom configuration parameters:
114 +
115 +|=Name|=Description|=Default Value
116 +|data-suggest-space|Restrict the suggestions to a specific location|None
117 +
118 +=== Suggest Attachments ===
119 +
120 +This suggester displays the attachment (file) name and the attachment location. The suggestion icon is either a preview, if the attachment is an image or an icon that depends on the attachment media type. The value that is saved in the end is the attachment reference. The hidden page user preference is taken into account (e.g. attachments from hidden pages are not displayed by default).
121 +
122 +{{code language="none"}}
123 +{{velocity}}
124 +{{html}}
125 +#set ($attachmentPickerParams = {
126 + ...
127 +})
128 +#attachmentPicker($attachmentPickerParams)
129 +{{/html}}
130 +{{/velocity}}
103 103  {{/code}}
104 104  
105 -The service page uses a query to get all the pages from the space provided using ##spacename## parameter in the URL. The generated response must be an **xml** file that has ##<results>## as a root node and ##<rs>## as children.
133 +{{image reference="attachmentPicker.png"/}}
106 106  
107 -{{code}}
108 -##
109 -## Service to generate the suggest list of files from a certain space.
110 -## @spacename
111 -## @input
112 -##
113 -#set($input = $request.get("input").toLowerCase())
114 -#set($spacename = $request.get("spacename"))
115 -$response.setContentType("text/xml") ## generate a xml file
116 -## select pages
117 -#if("$!input" == "")
118 - #set($query = "where doc.space='$spacename' and doc.name<>'WebHome' and doc.name<>'WebPreferences' order by doc.date desc")
119 -#else
120 - #set($query = "where doc.space='$spacename' and doc.name<>'WebHome' and doc.name<>'WebPreferences' and lower(doc.name) like '%" + $input + "%' order by doc.date desc")
121 -#end
122 -#set($searchresults = $xwiki.searchDocuments($query, 30, 0))
123 -<results space="$spacename">
124 - #foreach($result in $searchresults)
125 - #set($resultDoc = $xwiki.getDocument($result))
126 - #set($resultDocName = $resultDoc.name)
127 - #set($resultDocURL = $resultDoc.getURL())
128 - <rs id="1" info="$resultDocURL">$resultDocName</rs>
129 - #end
130 -</results>
135 +It supports local file upload, either by selecting the files from the file browser or by dropping them on the suggest input. It also supports drag and drop of attachment links from the Attachment tab at the bottom of the wiki page.
136 +
137 +Custom configuration parameters:
138 +
139 +|=Name|=Description|=Default Value
140 +|data-document-reference|The document where the selected values are saved and where new files are being uploaded. Stored attachment references will be relative this document.|Current page
141 +|data-search-scope|(((Where to look for attachments. The following is supported:
142 +* "wiki:wikiName" look for attachments in the specified wiki
143 +* "space:spaceReference": look for attachments in the specified space
144 +* "document:documentReference" look for attachments in the specified document)))|Current wiki
145 +|data-upload-allowed|Whether to allow the user to upload files|false
146 +|data-accept|(((Indicates the type of files that can be selected or uploaded. The value is a comma separated list of:
147 +* file name extensions (e.g. .png,.pdf)
148 +* complete or partial media types (e.g. image/,video/mpeg)
149 +
150 +If nothing is specified then no restriction is applied.)))|None
151 +
152 +=== Suggest Users ===
153 +
154 +This suggester displays the user avatar and their name. The value that is saved in the end is the user reference (the reference of the user profile page).
155 +
156 +{{code language="none"}}
157 +{{velocity}}
158 +{{html}}
159 +#set ($userPickerParams = {
160 + ...
161 +})
162 +#userPicker($userPickerParams)
163 +{{/html}}
164 +{{/velocity}}
131 131  {{/code}}
132 132  
133 -To provide autosuggest to several elements on the form, you can use JavaScript to loop through all the form elements and provide autosuggest if they meet certain conditions. In the example below, if there are form elements with id //Supplier// in the form, they get assigned a suggest widget that uses the //Suppliers// space as its source. If the element id matches //Product//, the suggest is told to use the //Products// space as its source instead.
167 +{{image reference="userPicker.png"/}}
134 134  
135 -This method can be very useful when a form contains a lot of similar elements that require autosuggest. If you make sure the naming is done consistently, you can also use javascript to assign autosuggest based on part of the element id, for example 'all elements ending with _foo' or 'all elements starting with Bar_'. You can use the velocity code from the example above with the code below.
169 +Custom configuration parameters:
136 136  
137 -{{code}}
138 -(function(){
139 - document.observe('dom:loaded', function () {
140 - myForm = document.getElementById('inline').elements;
141 - for(i=0; i<myForm.length; i++){
142 - if(myForm[i].id =='Supplier'){
143 - mySuggest(myForm[i], 'Suppliers');
144 - }
145 - if(myForm[i].id=='Product'){
146 - mySuggest(myForm[i], 'Products');
147 - }
148 - }
149 - }); // end of doc observe
150 -})();
171 +|=Name|=Description|=Default Value
172 +|data-userScope|Where to retrieve the user suggestions from. Supported values are: LOCAL_ONLY, GLOBAL_ONLY, LOCAL_AND_GLOBAL|User scope of the current wiki
151 151  
152 -function mySuggest(element, space) {
153 - if (!element.suggest) {
154 - element.suggest = new XWiki.widgets.Suggest(element, {
155 - script: "$xwiki.getURL('Sandbox.AutoSuggest', 'get', 'outputSyntax=plain&spacename=')"+space+"&",
156 - varname: "input",
157 - seps: " ,|",
158 - offsety: 13,
159 - minchars: 1
160 - });
161 - }
162 -}
174 +=== Suggest Groups ===
175 +
176 +The suggester displays the group logo and its name. The group logo is the first image attachment found on the group page. The value that is saved in the end is the group reference (the reference of the group page).
177 +
178 +{{code language="none"}}
179 +{{velocity}}
180 +{{html}}
181 +#set ($groupPickerParams = {
182 + ...
183 +})
184 +#groupPicker($groupPickerParams)
185 +{{/html}}
186 +{{/velocity}}
163 163  {{/code}}
164 164  
165 -=== Example ===
189 +{{image reference="groupPicker.png"/}}
166 166  
167 -Check out the example for custom information at [[Custom Information Example>>platform:DevGuide.AjaxSuggestCustomExample]]
191 +Custom configuration parameters:
168 168  
169 -== Suggest Users or Groups from the wiki ==
193 +|=Name|=Description|=Default Value
194 +|data-userScope|Where to retrieve the group suggestions from. Supported values are: LOCAL_ONLY, GLOBAL_ONLY, LOCAL_AND_GLOBAL. Note that GLOBAL_ONLY has the same effect as LOCAL_AND_GLOBAL because a wiki with only global users can have local groups|User scope of the current wiki
170 170  
171 -Local or global users and groups from the wiki can be suggested using the **uorgsuggest.vm** template.
196 +=== Suggest Property Values ===
172 172  
173 -Example:
198 +This suggester displays the label assocaited with a property value and saves the raw property value. The following property types are supported by this suggester: Static List and Database List. Note that these property types have default custom displayers associated that load the suggest widget when the "Use suggest" meta property is on. So normally the only thing you need to do is to display the property.
174 174  
175 -{{code}}
176 -$xwiki.jsx.use("$doc.fullName")##
200 +{{code language="none"}}
201 +{{velocity}}
202 +$doc.display('myDatabaseListProperty')
203 +{{/velocity}}
204 +{{/code}}
177 177  
178 -<input name="userInput" id="userInput" value="" type="text"/>
206 +{{image reference="propertyValuePicker.png"/}}
207 +
208 +Custom configuration parameters:
209 +
210 +|=Name|=Description|=Default Value
211 +|data-className|The class where the property is defined|None
212 +|data-propertyName|The property whose values are to be retrieved as suggestions|None
213 +|data-freeText|Whether free text is allowed or forbidden. Possible values are: allowed, forbidden|None
214 +
215 +== Custom Suggesters ==
216 +
217 +=== Suggest from Static Data ===
218 +
219 +Let's see how you can use the suggest widget with static data (i.e. data that is available when the web page is loaded).
220 +
221 +{{html wiki="true"}}
222 +<div>
223 + <ul class="nav nav-tabs" role="tablist">
224 + <li role="presentation" class="active">
225 + <a href="#staticDataV1" aria-controls="staticDataV1" role="tab" data-toggle="tab">V1: The Basics</a>
226 + </li>
227 + <li role="presentation">
228 + <a href="#staticDataV2" aria-controls="staticDataV2" role="tab" data-toggle="tab">V2: Separate Data</a>
229 + </li>
230 + <li role="presentation">
231 + <a href="#staticDataV3" aria-controls="staticDataV3" role="tab" data-toggle="tab">V3: Helper Macros</a>
232 + </li>
233 + <li role="presentation">
234 + <a href="#staticDataV4" aria-controls="staticDataV4" role="tab" data-toggle="tab">V4: Reusable Code</a>
235 + </li>
236 + </ul>
237 +
238 + <div class="tab-content">
239 + <div role="tabpanel" class="tab-pane active" id="staticDataV1">
240 +<p>The following code:</p>
241 +
242 +* loads the resources (JavaScript and CSS) needed by the suggest widget
243 +* outputs the HTML (select element) needed by the suggest widget
244 +
245 +<p>Note that the select element is marked with the ##xwiki-selectize## CSS class which activates the suggest widget.</p>
246 +
247 +{{code language="none"}}
248 +{{velocity}}
249 +#set ($discard = $xwiki.linkx.use($services.webjars.url('selectize.js', 'css/selectize.bootstrap3.css'),
250 + {'type': 'text/css', 'rel': 'stylesheet'}))
251 +#set ($discard = $xwiki.ssfx.use('uicomponents/suggest/xwiki.selectize.css', true))
252 +#set ($discard = $xwiki.jsfx.use('uicomponents/suggest/xwiki.selectize.js', true))
253 +{{/velocity}}
254 +
255 +{{html}}
256 +<select class="xwiki-selectize">
257 + <option value="">Select the country</option>
258 + <option value="fr">France</option>
259 + <option value="de">Germany</option>
260 + <option value="ro">Romania</option>
261 +</select>
262 +{{/html}}
179 179  {{/code}}
180 180  
181 -Here is the code that made the suggestion of global users from the wiki possible:
265 + </div>
182 182  
183 -{{code}}
184 -...
185 -<input name="userInput" id="userInput" value="" type="text"/>
186 -...
267 + <div role="tabpanel" class="tab-pane" id="staticDataV2">
268 + <p>In this version we separate the code that generates the data used to provide suggestions. The data is usually retrieved from the database.</p>
269 +
270 +{{code language="none"}}
271 +{{velocity}}
272 +{{html}}
273 +#set ($discard = $xwiki.linkx.use($services.webjars.url('selectize.js', 'css/selectize.bootstrap3.css'),
274 + {'type': 'text/css', 'rel': 'stylesheet'}))
275 +#set ($discard = $xwiki.ssfx.use('uicomponents/suggest/xwiki.selectize.css', true))
276 +#set ($discard = $xwiki.jsfx.use('uicomponents/suggest/xwiki.selectize.js', true))
277 +#set ($countries = [
278 + {'name': 'France', 'code': 'fr'},
279 + {'name': 'Germany', 'code': 'de'},
280 + {'name': 'Romania', 'code': 'ro'}
281 +])
282 +## See https://github.com/selectize/selectize.js/blob/master/docs/usage.md#configuration
283 +#set ($selectizeSettings = {})
284 +<select class="xwiki-selectize" data-xwiki-selectize="$escapetool.xml($jsontool.serialize($selectizeSettings))">
285 + <option value="">Select the country</option>
286 + #foreach ($country in $countries)
287 + <option value="$!escapetool.xml($country.code)">$!escapetool.xml($country.name)</option>
288 + #end
289 +</select>
290 +{{/html}}
291 +{{/velocity}}
187 187  {{/code}}
188 188  
189 -{{code}}
190 -(function(){
191 - document.observe('dom:loaded', function () {
192 - if($('userInput')) {
193 - Event.observe($('userInput'), "focus", function() {
194 - new XWiki.widgets.Suggest(this, {
195 - script: '$xwiki.getURL("${doc.fullName}", "view")?xpage=uorgsuggest&classname=XWiki.XWikiUsers&wiki=global&uorg=user&',
196 - varname: "input",
197 - seps: " ,|",
198 - delay : 200,
199 - timeout: 5000,
200 - offsety: 13
201 - });
202 - });
203 - }
204 - }); // end of doc observe
205 -})();
294 + </div>
295 +
296 + <div role="tabpanel" class="tab-pane" id="staticDataV3">
297 + <p>In this version we use some helper Velocity macros: ##picker_import## and ##suggestInput##. Notice the way we specify the selected value and the placeholder by passing parameters to the ##suggestInput## macro.</p>
298 +
299 +{{code language="none"}}
300 +{{velocity output="false"}}
301 +#set ($countries = [
302 + {'name': 'France', 'code': 'fr'},
303 + {'name': 'Germany', 'code': 'de'},
304 + {'name': 'Romania', 'code': 'ro'}
305 +])
306 +
307 +#macro (countryPicker_displayOptions $selectedValues)
308 + #foreach ($country in $countries)
309 + <option value="$!escapetool.xml($country.code)"#if ($selectedValues.contains($country.code)) selected="selected"#end
310 + >$!escapetool.xml($country.name)</option>
311 + #end
312 +#end
313 +{{/velocity}}
314 +
315 +{{velocity}}
316 +{{html}}
317 +#picker_import
318 +#set ($discard = $xwiki.jsfx.use('uicomponents/suggest/xwiki.selectize.js', true))
319 +## See https://github.com/selectize/selectize.js/blob/master/docs/usage.md#configuration
320 +#set ($selectizeSettings = {})
321 +#set ($suggestInputAttributes = {
322 + 'name': 'country',
323 + 'class': 'xwiki-selectize',
324 + 'data-xwiki-selectize': $selectizeSettings,
325 + 'value': 'ro',
326 + 'placeholder': 'Select the country'
327 +})
328 +#suggestInput($suggestInputAttributes 'countryPicker_displayOptions')
329 +{{/html}}
330 +{{/velocity}}
206 206  {{/code}}
207 207  
208 -== Search suggest shows document title but searches by document name ==
333 + </div>
209 209  
210 -Example:
335 + <div role="tabpanel" class="tab-pane" id="staticDataV4">
336 + <p>In this last version we make the code reusable by creating the ##countryPicker## and ##countryPicker_import## Velocity macros.</p>
211 211  
212 -The request
338 +{{code language="none"}}
339 +{{velocity output="false"}}
340 +#set ($countries = [
341 + {'name': 'France', 'code': 'fr'},
342 + {'name': 'Germany', 'code': 'de'},
343 + {'name': 'Romania', 'code': 'ro'}
344 +])
213 213  
214 -{{code}}
346 +#macro (countryPicker_displayOptions $selectedValues)
347 + #foreach ($country in $countries)
348 + <option value="$!escapetool.xml($country.code)"#if ($selectedValues.contains($country.code)) selected="selected"#end
349 + >$!escapetool.xml($country.name)</option>
350 + #end
351 +#end
215 215  
216 -#set($suggestURL = $xwiki.getURL('Main.WebHome', 'view', "&xpage=suggest&classname=SomeSpace.SomeClass&fieldname=entity&firCol=obj.name&secCol=doc.title"))
353 +#macro (countryPicker_import)
354 + #picker_import
355 + #set ($discard = $xwiki.jsfx.use('uicomponents/suggest/xwiki.selectize.js', true))
356 +#end
217 217  
218 -onfocus='new XWiki.widgets.Suggest(this, {script:"$escapetool.javascript("${suggestURL}&")", varname:"input", callback: function(obj) {resource.onChangeEntity(obj.info, obj.value);}} )'
358 +#macro (countryPicker $parameters)
359 + #countryPicker_import
360 + #if ("$!parameters" == "")
361 + #set ($parameters = {})
362 + #end
363 + #set ($discard = $parameters.put('class', "$!parameters.get('class') xwiki-selectize suggest-countries"))
364 + #if (!$parameters.containsKey('placeholder'))
365 + #set ($parameters.placeholder = 'Select the country')
366 + #end
367 + #suggestInput($parameters 'countryPicker_displayOptions')
368 +#end
369 +{{/velocity}}
219 219  
371 +{{velocity}}
372 +{{html}}
373 +#set ($countryPickerParams = {
374 + 'name': 'country',
375 + 'value': 'ro'
376 +})
377 +#countryPicker($countryPickerParams)
378 +{{/html}}
379 +{{/velocity}}
220 220  {{/code}}
221 221  
222 -The JavascriptExtension object
382 + </div>
383 + </div>
384 +</div>
385 +{{/html}}
223 223  
224 -{{code}}
387 +=== Suggest from Remote Data ===
225 225  
226 -// entityValue is the document name and entityInfo is the document title
227 - onChangeEntity: function(entityValue, entityInfo) {
228 - // Update entity value
229 - $('SomeSpace.SomeClass_0_entity_suggest').value = entityInfo;
230 - var serviceURL = new XWiki.Document('SomePageOne', 'SomeSpace').getURL('view');
231 - var ajx = new Ajax.Request(serviceURL, {
232 - method: 'get',
233 - parameters: 'xpage=plain&outputSyntax=plain&entity=' + encodeURIComponent(entityValue)
234 - }
389 +Most of the time you will want to retrive suggestions when the user types rather than loading all the suggestions on page load. Let's see how you can do this.
390 +
391 +{{html wiki="true"}}
392 +<div>
393 + <ul class="nav nav-tabs" role="tablist">
394 + <li role="presentation" class="active">
395 + <a href="#remoteDataV1" aria-controls="remoteDataV1" role="tab" data-toggle="tab">V1: The Basics</a>
396 + </li>
397 + <li role="presentation">
398 + <a href="#remoteDataV2" aria-controls="remoteDataV2" role="tab" data-toggle="tab">V2: Reusable Code</a>
399 + </li>
400 + </ul>
401 +
402 + <div class="tab-content">
403 + <div role="tabpanel" class="tab-pane active" id="remoteDataV1">
404 + <p>In order to retrieve suggestions as you type you need to write some JavaScript code that makes an asynchronous HTTP request to retrive the data. You can put this code in a JavaScript Skin extension object. Then you need to load this JavaScript code on the page where you want to activate the suggest widget.</p>
405 +
406 +{{code language="js"}}
407 +require.config({
408 + paths: {
409 + 'xwiki-selectize': "$xwiki.getSkinFile('uicomponents/suggest/xwiki.selectize.js', true)" +
410 + "?v=$escapetool.url($xwiki.version)"
411 + }
412 +});
413 +
414 +require(['jquery', 'xwiki-selectize'], function($) {
415 + var settings = {
416 + load: function(typedText, callback) {
417 + $.getJSON('your/service/url', {
418 + text: typedText
419 + }).done(callback).fail(callback);
420 + },
421 + loadSelected: function(selectedValue, callback) {
422 + $.getJSON('your/service/url', {
423 + text: typedText,
424 + exactMatch: true
425 + }).done(callback).fail(callback);
235 235   }
427 + };
236 236  
429 + $('.suggest-countries').xwikiSelectize(settings);
430 +});
237 237  {{/code}}
238 238  
239 -= Javascript parameters for the ##XWiki.widgets.Suggest## constructor =
433 +{{code language="none"}}
434 +{{velocity}}
435 +#set ($discard = $xwiki.linkx.use($services.webjars.url('selectize.js', 'css/selectize.bootstrap3.css'),
436 + {'type': 'text/css', 'rel': 'stylesheet'}))
437 +#set ($discard = $xwiki.ssfx.use('uicomponents/suggest/xwiki.selectize.css', true))
438 +#set ($discard = $xwiki.jsx.use('Path.To.Your.JSX'))
439 +{{/velocity}}
240 240  
241 -|=Parameter|=Details|=Default value
242 -|##className##|The CSS classname of the suggest list.|##ajaxsuggest##
243 -|##minchars##|The minimum number of characters after which to trigger the suggest.|##1##
244 -|##delay##|Throttle delay: how much to wait after a keypress before requesting suggestions from the server, in milliseconds.|##500##
245 -|##timeout##|How long to display the list of suggestions, in milliseconds. If the user doesn't interact with the suggestions before the timeout expires, the list will be cleared.|##2500## (2.5 seconds).
246 -|##offsety##|How much to shift the list of suggestions vertically from the normal position, in pixels. This allows, for example, room for extra decorations between the input and the list.|##0##
247 -|##shownoresults##|What to do when no results match the current input: display a "no results" message (##true##), or simply hide the suggest box when no suggestions are available (##false##).|##true##
248 -|##noresults##|Default displayed message when ##shownoresults## is enabled and there are no results to display.|##No results!## (translation key ##core.widgets.suggest.noResults##)
249 -|##hideButton##|Controls whether a //hide suggestions// button (or two) is displayed or not. If used, must be a map (JS Object) with two possible keys: ##hideButton.positions## is an array that accepts as values ##top## and ##bottom##, specifying where to place hide buttons, and ##hideButton.text## is the text that should be displayed.|##{positions: ['top'], text: 'hide suggestions'}## (translation key ##core.widgets.suggest.hide##)
250 -|##cache##|Cache the list suggestions returned for a specific input for the lifetime of the current page.|##false##
251 -|##seps##|If suggestions should be returned for each token instead of the full text in the input, set this to a list of characters that should be used for splitting the input into tokens. Leave empty to skip tokenizing and use the whole text instead.|Empty string
252 -|##parentContainer##|The id of the element that will hold the suggest element. Useful when the enhanced input is not statically positioned, for example in a modal dialog|##body##
253 -|##sources##|Array of sources from where to fetch suggestions. If there are any entries in this array, then the suggest functions in //multi-source// mode; if not, then the suggest is in //single-source// mode. Every entry should be a map (JS Object), and the following parameters should be used as keys in every such map instead of keys in the global options.|None, by default the suggest is in single-source mode
254 -|##script##|URL for the ajax request that will get the suggested list. Must end with ##&## because ##varname## parameter will be appended. Use ##suggest.vm## to get field values from a wiki class or a custom URL to generate the suggested list.|None, this parameter is mandatory
255 -|##varname##|The name of the request parameter holding the input stub.|##input##
256 -|##method##|The HTTP method for the AJAX request.|##get##
257 -|##resultsParameter##|The name of the JSON variable or XML element holding the results.|##results## for XML results, must be changed to ##searchResults## for the REST search
258 -|##resultId##|The name of the JSON parameter or XML attribute holding the result identifier.|##id## for both the old suggest and the REST search
259 -|##resultValue##|The name of the JSON parameter or XML attribute holding the result value.|##value## for the old suggest, must be changed to ##pageFullName## for the REST search
260 -|##resultInfo##|The name of the JSON parameter or XML attribute holding the result auxiliary information.|##info## for the old suggest, must be changed to ##pageFullName## for the REST search
261 -|##icon##|An icon to display for every entry in the results fetched from this source.|None, no icon is displayed
262 -|##highlight##|Should results fragments be highlighted when matching typed input.|##true##
263 -|##align##|Control where the suggest box will be displayed. Possible values are: ##left##, ##center##, ##right##.
264 -Since 6.2: there is also the ##auto## value, that will place the search box from the left of the input box unless there is not enough place to display it entirely, otherwise it will be displayed from the right.|##right##
265 -|##propagateEventKeyCodes##|A sublist of key codes, from the list of keys handled by this widget, for which to propagate the keyboard event. Useful when another keyboard event listener exists on the input field, even if it may be registered at a difference level. See the ##onKeyPress## in the code for a clearer picture.|Empty list. By default, none of the handled key events propagate. All other, not handled, events do.
441 +{{html}}
442 +<select class="suggest-countries">
443 + <option value="">Select the country</option>
444 +</select>
445 +{{/html}}
446 +{{/code}}
266 266  
267 -= Velocity macros =
448 + </div>
268 268  
269 -{{info}}Since 10.9{{/info}}, you can use velocity macros to insert the suggest widgets.
450 + <div role="tabpanel" class="tab-pane" id="remoteDataV2">
451 + <p>In this last version we make the code reusable by creating a jQuery plugin and some helper Velocity macros.</p>
270 270  
271 -== Page picker widget ==
453 +{{code language="js"}}
454 +require.config({
455 + paths: {
456 + 'xwiki-selectize': "$xwiki.getSkinFile('uicomponents/suggest/xwiki.selectize.js', true)" +
457 + "?v=$escapetool.url($xwiki.version)"
458 + }
459 +});
272 272  
273 -[[image:page-picker.jpg||height="246" width="796"]]
461 +define('xwiki-suggestCountries', ['jquery', 'xwiki-selectize'], function($) {
462 + var getSettings = function(select) {
463 + return {
464 + load: function(typedText, callback) {
465 + $.getJSON('your/service/url', {
466 + text: typedText
467 + }).done(callback).fail(callback);
468 + },
469 + loadSelected: function(selectedValue, callback) {
470 + $.getJSON('your/service/url', {
471 + text: typedText,
472 + exactMatch: true
473 + }).done(callback).fail(callback);
474 + }
475 + };
476 + };
274 274  
275 -The page picker widget can be inserted on a page using the following code:
276 -{{code}}{{velocity}}
277 -{{html}}
278 - #pagePicker()
279 -{{/html}}
280 -{{/velocity}}{{/code}}
478 + $.fn.suggestCountries = function(settings) {
479 + return this.each(function() {
480 + $(this).xwikiSelectize($.extend(getSettings($(this)), settings));
481 + });
482 + };
483 +});
281 281  
282 -You can specify the HTML attributes of the select by doing this:
283 -{{code}}#set ($parameters = {'multiple': 'multiple', 'id': 'my-id'})
284 -#pagePicker($parameters){{/code}}
485 +require(['jquery', 'xwiki-suggestCountries', 'xwiki-events-bridge'], function($) {
486 + var init = function(event, data) {
487 + var container = $((data && data.elements) || document);
488 + container.find('.suggest-countries').suggestCountries();
489 + };
285 285  
286 -{{info}}Since 10.11.1{{/info}}, you can also add a special parameter to restrict the suggestions to a page and its children:
287 -{{code}}#set ($parameters = {'data-suggest-space': 'Sandbox'})
288 -#pagePicker($parameters){{/code}}
289 -[[image:1547135793681-650.png||height="222" width="373"]]
491 + $(document).on('xwiki:dom:loaded xwiki:dom:updated', init);
492 + XWiki.domIsLoaded && init();
493 +});
494 +{{/code}}
290 290  
291 -= Tips =
496 +{{code language="none"}}
497 +{{velocity output="false"}}
498 +#macro (countryPicker_import)
499 + #picker_import
500 + #set ($discard = $xwiki.jsx.use('Path.To.Your.JSX'))
501 +#end
292 292  
293 -* Suggest event:(((
294 -xwiki:suggest:selected##{{code}}Event.observe($('myInput'), "xwiki:suggest:selected", function(event) {
295 - // do something with event.memo.value or event.memo.id or event.memo.info ...
296 - });{{/code}}##
297 -)))
298 -* Check out the code:
299 -** [[for your wiki instance>>http://localhost:8080/xwiki/resources/js/xwiki/suggest/ajaxSuggest.js]]
300 -** the suggest resources on GitHub (for example tag xwiki-platform-4.1): {{scm branch="xwiki-platform-4.1" path="xwiki-platform-core/xwiki-platform-web/src/main/webapp/resources/uicomponents/suggest/"/}}
503 +#macro (countryPicker $parameters)
504 + #countryPicker_import
505 + #if ("$!parameters" == "")
506 + #set ($parameters = {})
507 + #end
508 + #set ($discard = $parameters.put('class', "$!parameters.get('class') suggest-countries"))
509 + #if (!$parameters.containsKey('placeholder'))
510 + #set ($parameters.placeholder = 'Select the country')
511 + #end
512 + #suggestInput($parameters)
513 +#end
514 +{{/velocity}}
301 301  
302 -= Bugs we know about =
516 +{{velocity}}
517 +{{html}}
518 +#set ($countryPickerParams = {
519 + 'name': 'country',
520 + 'value': 'ro'
521 +})
522 +#countryPicker($countryPickerParams)
523 +{{/html}}
524 +{{/velocity}}
525 +{{/code}}
303 303  
304 -The suggest feature will not work if the page called by the widget is not saved with programming rights (see details on [[this issue>>https://jira.xwiki.org/browse/XE-539]]).
527 + </div>
528 + </div>
529 +</div>
530 +{{/html}}
531 +
532 +The remote data should have the following JSON format:
533 +
534 +{{code language="js"}}
535 +[
536 + {
537 + 'label': '...',
538 + 'value': '...',
539 + 'url': '...',
540 + 'icon': {
541 + 'iconSetName': '...',
542 + 'iconSetType': 'IMAGE or FONT',
543 + 'cssClass': 'for font icons',
544 + 'url': 'for image icons'
545 + },
546 + 'hint': '...'
547 + },
548 + ...
549 +]
550 +{{/code}}

Get Connected