diff --git a/clint/updates/1.0.3/CHANGELOG.md b/clint/updates/1.0.3/CHANGELOG.md new file mode 100644 index 00000000..4fcfb61c --- /dev/null +++ b/clint/updates/1.0.3/CHANGELOG.md @@ -0,0 +1,11 @@ +# Update 1.0.3 + +**Release date:** 2025-12-08 + +## Summary + +Added an ajax option to the autocomplete component + +## Fixed + +- Fix [Issue 544](https://github.com/statikbe/craft/issues/544) diff --git a/clint/updates/1.0.3/update.json b/clint/updates/1.0.3/update.json new file mode 100644 index 00000000..00a8606a --- /dev/null +++ b/clint/updates/1.0.3/update.json @@ -0,0 +1,3 @@ +{ + "description": "Added an ajax option to the autocomplete component." +} diff --git a/docs/src/frontend/components/autocomplete.md b/docs/src/frontend/components/autocomplete.md index 27761b63..356d7238 100644 --- a/docs/src/frontend/components/autocomplete.md +++ b/docs/src/frontend/components/autocomplete.md @@ -10,6 +10,7 @@ The Autocomplete component transforms a ` ``` +## AJAX Loading + +The autocomplete component can load options dynamically from an API endpoint using the `data-ajax-url` attribute. This is ideal for large datasets, search-as-you-type functionality, or data that changes frequently. + +### Basic AJAX Example + +```html + +``` + +### API Response Format + +Your API endpoint must return JSON in this exact format: + +```json +{ + "data": [ + { "value": 1, "option": "News Title 1" }, + { "value": 2, "option": "News Title 2" }, + { "value": 3, "option": "News Title 3" } + ], + "pagination": { + "page": 1, + "perPage": 20, + "total": 50, + "totalPages": 3 + } +} +``` + +**Required fields:** + +- `data` - Array of option objects + - `value` - The option value (number or string, converted to string internally) + - `option` - The display text for the option +- `pagination` - Pagination metadata + - `page` - Current page number + - `perPage` - Number of items per page + - `total` - Total number of items available + - `totalPages` - Total number of pages + +### How AJAX Loading Works + +1. **Initial Load**: Component fetches first page from `data-ajax-url` on initialization +2. **Search**: As user types, component sends `?q={searchTerm}&page=1` to API +3. **Pagination**: As user scrolls near bottom of list (200px threshold), next page loads automatically +4. **Merging**: New options are appended to existing list for infinite scroll experience +5. **Selected Options**: Selected options are preserved and excluded from duplicate loading + +### API Parameters + +The component automatically sends these query parameters: + +| Parameter | Type | Description | Example | +| --------- | ------ | ------------------------------------ | ------------- | +| `q` | string | Search query (optional, when typing) | `?q=breaking` | +| `page` | number | Page number for pagination | `?page=2` | + +### Example: Craft CMS API Endpoint + +Create a Twig template at `/templates/api/news.twig`: + +```twig +{%- header "Content-Type: application/json" -%} +{%- set searchQuery = craft.app.request.getParam('q') -%} +{%- set perPage = craft.app.request.getParam('perPage', 20) -%} +{%- set page = craft.app.request.getParam('page', 1) -%} +{%- set newsQuery = craft.entries() + .section('news') + .orderBy('title ASC') -%} +{%- if searchQuery -%} + {%- set newsQuery = newsQuery.search('title:*' ~ searchQuery ~ '*') -%} +{%- endif -%} +{%- set totalEntries = newsQuery.count() -%} +{%- set newsEntries = newsQuery.offset((page - 1) * perPage).limit(perPage).all() -%} +{ + "data": [ +{%- for entry in newsEntries -%} + {"value": {{ entry.id }}, "option": {{ entry.title|json_encode|raw }}} + {%- if not loop.last -%},{%- endif -%} +{%- endfor -%} + ], + "pagination": { + "page": {{ page }}, + "perPage": {{ perPage }}, + "total": {{ totalEntries }}, + "totalPages": {{ (totalEntries / perPage)|round(0, 'ceil') }} + } +} +``` + +Add route in `config/routes.php`: + +```php +return [ + 'api/news.json' => ['template' => 'api/news'], +]; +``` + +### AJAX with Pre-selected Options + +You can combine AJAX loading with pre-selected options in the select element: + +```html + +``` + +The component will: + +1. Load AJAX options from API +2. Detect the pre-selected option from the select element +3. Show it in the input field (single select) or as a tag (multi-select) +4. Exclude it from duplicate loading when paginating + +### Infinite Scroll Pagination + +When using AJAX, the dropdown list supports infinite scroll: + +- **Scroll Trigger**: When user scrolls within 200px of bottom +- **Automatic Loading**: Next page fetches and appends automatically +- **No Duplicates**: Already selected options are excluded +- **Page Tracking**: Component tracks current page and prevents duplicate requests +- **End Detection**: Stops loading when `page >= totalPages` + +::: tip Pagination Performance +For best performance: + +- Keep `perPage` between 10-50 items +- Use server-side indexing for fast searches +- Consider caching frequently accessed pages +- Return consistent `totalPages` for accurate scroll detection + ::: + +### AJAX with Multiple Select + +```html + +``` + +Behavior: + +- Selected items appear as tags +- Tags are excluded from API results (no duplicates) +- Search resets to page 1 +- Pagination continues to work as user scrolls + +### AJAX Error Handling + +The component does not currently implement explicit error handling. If the API request fails: + +- Console errors will appear +- Options list will remain empty or show previous results +- No user-facing error message displayed + +**Best practices:** + +- Ensure API endpoint is reliable +- Test API response format carefully +- Monitor console for network errors +- Consider adding server-side logging + +::: warning API Format Required +The AJAX feature **requires** the exact JSON format shown above. Incorrect format will cause the component to fail silently or throw JavaScript errors. +::: + +::: tip Combining with Static Options +You can combine AJAX options with static `