More features

- Custom renderers depending on specified model name
- Paginate API results
This commit is contained in:
Oliver 2021-06-26 14:09:35 +10:00
parent b29db6f258
commit 565631ef87
5 changed files with 150 additions and 15 deletions

View File

@ -973,3 +973,10 @@ input[type="date"].form-control, input[type="time"].form-control, input[type="da
.select2-container { .select2-container {
width: 100%; width: 100%;
} }
.select2-thumbnail {
max-width: 24px;
max-height: 24px;
border-radius: 4px;
margin-right: 10px;
}

View File

@ -105,6 +105,7 @@ settings_urls = [
dynamic_javascript_urls = [ dynamic_javascript_urls = [
url(r'^api.js', DynamicJsView.as_view(template_name='js/api.js'), name='api.js'), url(r'^api.js', DynamicJsView.as_view(template_name='js/api.js'), name='api.js'),
url(r'^forms.js', DynamicJsView.as_view(template_name='js/forms.js'), name='forms.js'), url(r'^forms.js', DynamicJsView.as_view(template_name='js/forms.js'), name='forms.js'),
url(r'^model_renderers.js', DynamicJsView.as_view(template_name='js/model_renderers.js'), name='model_renderers.js'),
url(r'^modals.js', DynamicJsView.as_view(template_name='js/modals.js'), name='modals.js'), url(r'^modals.js', DynamicJsView.as_view(template_name='js/modals.js'), name='modals.js'),
url(r'^barcode.js', DynamicJsView.as_view(template_name='js/barcode.js'), name='barcode.js'), url(r'^barcode.js', DynamicJsView.as_view(template_name='js/barcode.js'), name='barcode.js'),
url(r'^bom.js', DynamicJsView.as_view(template_name='js/bom.js'), name='bom.js'), url(r'^bom.js', DynamicJsView.as_view(template_name='js/bom.js'), name='bom.js'),

View File

@ -150,6 +150,7 @@
<!-- translated --> <!-- translated -->
<script type='text/javascript' src="{% i18n_static 'api.js' %}"></script> <script type='text/javascript' src="{% i18n_static 'api.js' %}"></script>
<script type='text/javascript' src="{% i18n_static 'forms.js' %}"></script> <script type='text/javascript' src="{% i18n_static 'forms.js' %}"></script>
<script type='text/javascript' src="{% i18n_static 'model_renderers.js' %}"></script>
<script type='text/javascript' src="{% i18n_static 'barcode.js' %}"></script> <script type='text/javascript' src="{% i18n_static 'barcode.js' %}"></script>
<script type='text/javascript' src="{% i18n_static 'bom.js' %}"></script> <script type='text/javascript' src="{% i18n_static 'bom.js' %}"></script>
<script type='text/javascript' src="{% i18n_static 'company.js' %}"></script> <script type='text/javascript' src="{% i18n_static 'company.js' %}"></script>

View File

@ -364,7 +364,8 @@ function initializeRelatedField(modal, name, field, options) {
// TODO: Add 'placeholder' support for entry select2 fields // TODO: Add 'placeholder' support for entry select2 fields
// TODO: Add 'pagination' support for the query // limit size for AJAX requests
var pageSize = options.pageSize || 25;
select.select2({ select.select2({
ajax: { ajax: {
@ -377,31 +378,52 @@ function initializeRelatedField(modal, name, field, options) {
cache: true, cache: true,
// matcher: partialMatcher, // matcher: partialMatcher,
data: function(params) { data: function(params) {
if (!params.page) {
offset = 0;
} else {
offset = (params.page - 1) * pageSize;
}
// Re-format search term into InvenTree API style // Re-format search term into InvenTree API style
return { return {
search: params.term, search: params.term,
offset: offset,
limit: pageSize,
}; };
}, },
processResults: function(data) { processResults: function(response) {
// Convert the returned InvenTree data into select2-friendly format // Convert the returned InvenTree data into select2-friendly format
var rows = [];
// Only ever show the first x items var data = [];
for (var idx = 0; idx < data.length && idx < 50; idx++) {
var row = data[idx];
// Reformat to match select2 requirements var more = false;
row.id = row.id || row.pk;
// TODO: Fix me? if ('count' in response && 'results' in response) {
row.text = `This is ${field.api_url}${row.id}/`; // Response is paginated
data = response.results;
rows.push(row); // Any more data available?
if (response.next) {
more = true;
}
} else {
// Non-paginated response
data = response;
}
// Each 'row' must have the 'id' attribute
for (var idx = 0; idx < data.length; idx++) {
data[idx].id = data[idx].pk;
} }
// Ref: https://select2.org/data-sources/formats // Ref: https://select2.org/data-sources/formats
var results = { var results = {
results: rows, results: data,
pagination: {
more: more,
}
}; };
return results; return results;
@ -442,7 +464,7 @@ function initializeRelatedField(modal, name, field, options) {
* - parameters: The field definition (OPTIONS) request * - parameters: The field definition (OPTIONS) request
* - options: Other options provided at time of modal creation by the client * - options: Other options provided at time of modal creation by the client
*/ */
function renderModelData(name, model, data, paramaters, options) { function renderModelData(name, model, data, parameters, options) {
if (!data) { if (!data) {
return '{% trans "Searching" %}...'; return '{% trans "Searching" %}...';
@ -452,20 +474,41 @@ function renderModelData(name, model, data, paramaters, options) {
var html = null; var html = null;
var renderer = null;
// Find a custom renderer
switch (model) { switch (model) {
case 'company': case 'company':
html = `<span>${data.name}</span> - <i>${data.description}</i>`; renderer = renderCompany;
break;
case 'stockitem':
renderer = renderStockItem;
break;
case 'stocklocation':
renderer = renderStockLocation;
break;
case 'part':
renderer = renderPart;
break;
case 'partcategory':
renderer = renderPartCategory;
break;
default: default:
break; break;
} }
if (renderer != null) {
html = renderer(name, data, parameters, options);
}
if (html != null) { if (html != null) {
// Render HTML to an object // Render HTML to an object
var $state = $(html); var $state = $(html);
return $state; return $state;
} else { } else {
console.log(`ERROR: Rendering not implemented for model '${model}'`);
// Simple text rendering // Simple text rendering
return data.text; return data.id;
} }
} }

View File

@ -0,0 +1,83 @@
/*
* This file contains functions for rendering various InvenTree database models,
* in particular for displaying them in modal forms in a 'select2' context.
*
* Each renderer is provided with three arguments:
*
* - name: The 'name' of the model instance in the referring model
* - data: JSON data which represents the model instance. Returned via a GET request.
* - parameters: The field parameters provided via an OPTIONS request to the endpoint.
* - options: User options provided by the client
*/
// Renderer for "Company" model
function renderCompany(name, data, parameters, options) {
var html = `<span>${data.name}</span> - <i>${data.description}</i>`;
return html;
}
// Renderer for "StockItem" model
function renderStockItem(name, data, parameters, options) {
// TODO - Include part detail, location, quantity
// TODO - Include part image
}
// Renderer for "StockLocation" model
function renderStockLocation(name, data, parameters, options) {
var html = `<span>${data.name}</span>`;
if (data.description) {
html += ` - <i>${data.description}</i>`;
}
if (data.pathstring) {
html += `<p><small>${data.pathstring}</small></p>`;
}
return html;
}
// Renderer for "Part" model
function renderPart(name, data, parameters, options) {
var image = data.image;
if (!image) {
image = `/static/img/blank_image.png`;
}
var html = `<img src='${image}' class='select2-thumbnail'>`;
html += ` <span>${data.full_name ?? data.name}</span>`;
if (data.description) {
html += ` - <i>${data.description}</i>`;
}
return html;
}
// Renderer for "PartCategory" model
function renderPartCategory(name, data, parameters, options) {
var html = `<span>${data.name}</span>`;
if (data.description) {
html += ` - <i>${data.description}</i>`;
}
if (data.pathstring) {
html += `<p><small>${data.pathstring}</small></p>`;
}
return html;
}