Skip to main content

Modal\ProductMultiSelect Form Field

Modal\ProductMultiSelectField extends the J2Commerce ModalMultiSelectField base class to provide a multi-product picker. The field opens the products view in layout=modal_multiselect mode so the user can select multiple products in one session. Selected products are displayed in a sortable table below the button and stored as an array of hidden inputs.

Key Classes

ClassFilePurpose
ProductMultiSelectFieldadministrator/components/com_j2commerce/src/Field/Modal/ProductMultiSelectField.phpModal URL, product table rendering, JS wiring
ModalMultiSelectFieldlibraries/j2commerce/src/Field/ModalMultiSelectField.phpBase class — JS handler, initItemMultiField, remove/clear callbacks
modal-multiselect-field.min.jsmedia/lib_j2commerce/modal-multiselect-field.min.jsVanilla JS — window.initItemMultiField

Architecture

/administrator/index.php
?option=com_j2commerce
&view=products
&layout=modal_multiselect
&tmpl=component
&function=jSelectItemMultiCallback_{field_id}
&{csrf_token}=1
[&forcedLanguage={language}]

Value Format

Input arrives as a comma-separated string or an array of integers. setup() normalises both forms into array<int> before calling parent::setup():

// Comma-separated string → array
'12,45,89' → [12, 45, 89]

// Single value → array
'12' → [12]

Hidden inputs are rendered one per product:

<input type="hidden" name="jform[request][product_ids][0]" value="12" id="field_hidden_0" data-title="Product A">
<input type="hidden" name="jform[request][product_ids][1]" value="45" id="field_hidden_1" data-title="Product B">

Product Table

After the modal button, the field renders a Bootstrap 5 table showing selected products:

<div id="{field_id}_table">
<div class="my-2"><strong>Selected Products (2):</strong></div>
<table class="table table-sm table-striped">
<thead>
<tr>
<th class="w-10">ID</th>
<th>Name</th>
<th class="text-end w-6"><button ...><!-- clear all --></button></th>
<th class="w-1"></th>
</tr>
</thead>
<tbody>
<tr>
<td class="fw-bold">12</td>
<td>Product A</td>
<td class="text-end"><button ...><!-- remove --></button></td>
<td><input type="hidden" ...></td>
</tr>
</tbody>
</table>
</div>

When no products are selected, a COM_J2COMMERCE_NO_PRODUCTS_SELECTED message is shown instead.

Title Resolution

getValueTitles() loads titles for all selected IDs in a single query:

SELECT p.j2commerce_product_id, c.title
FROM #__j2commerce_products AS p
INNER JOIN #__content AS c ON p.product_source_id = c.id
WHERE p.j2commerce_product_id IN (12, 45, 89)

Results are indexed by j2commerce_product_id for O(1) lookup when building the table rows.

JavaScript

loadJavaScript() registers the shared lib_j2commerce.modal-multiselect-field module script (loaded once via WAM deduplication) and passes language strings to JS via Text::script():

Language constantJS key
COM_J2COMMERCE_SELECTED_PRODUCTSCOM_J2COMMERCE_SELECTED_PRODUCTS
COM_J2COMMERCE_PRODUCT_FIELD_IDCOM_J2COMMERCE_PRODUCT_FIELD_ID
COM_J2COMMERCE_PRODUCT_FIELD_NAMECOM_J2COMMERCE_PRODUCT_FIELD_NAME
COM_J2COMMERCE_PRODUCTS_CLEAR_ALLCOM_J2COMMERCE_PRODUCTS_CLEAR_ALL
COM_J2COMMERCE_PRODUCT_CLEARCOM_J2COMMERCE_PRODUCT_CLEAR

An inline script calls window.initItemMultiField('field_id') on DOMContentLoaded and assigns handler.customUpdateTable to the product-specific table renderer.

XML Usage

<!-- File: administrator/components/com_j2commerce/forms/coupon.xml (example) -->

<form addfieldprefix="J2Commerce\Component\J2commerce\Administrator\Field\Modal">
<fieldset name="products">
<field
name="applicable_products"
type="Modal_ProductMultiselect"
label="COM_J2COMMERCE_FIELD_APPLICABLE_PRODUCTS_LABEL"
description="COM_J2COMMERCE_FIELD_APPLICABLE_PRODUCTS_DESC"
/>
</fieldset>
</form>

XML Attributes

AttributeTypeDefaultDescription
typestringMust be Modal_ProductMultiselect
languagestringForce a content language in the modal
propagateboolfalseEnable propagate action for multilingual setups
requiredboolfalseMark field as required

Namespace Registration

The Modal subdirectory requires the full namespace path:

<form addfieldprefix="J2Commerce\Component\J2commerce\Administrator\Field\Modal">

Or per-field:

<field
name="product_ids"
type="Modal_ProductMultiselect"
addfieldprefix="J2Commerce\Component\J2commerce\Administrator\Field\Modal"
label="..."
/>

Usage in Plugin Forms

<!-- File: plugins/j2commerce/app_yourplugin/config.xml -->

<?xml version="1.0" encoding="UTF-8"?>
<config>
<fields name="params">
<fieldset name="basic" label="COM_PLUGINS_BASIC_FIELDSET_LABEL">
<field
name="excluded_products"
type="Modal_ProductMultiselect"
addfieldprefix="J2Commerce\Component\J2commerce\Administrator\Field\Modal"
label="PLG_J2COMMERCE_APP_YOURPLUGIN_FIELD_EXCLUDED_PRODUCTS_LABEL"
description="PLG_J2COMMERCE_APP_YOURPLUGIN_FIELD_EXCLUDED_PRODUCTS_DESC"
/>
</fieldset>
</fields>
</config>

Reading values in the plugin:

// File: plugins/j2commerce/app_yourplugin/src/Extension/AppYourPlugin.php

// Params stores comma-separated IDs as a string
$raw = $this->params->get('excluded_products', '');
$productIds = array_filter(array_map('intval', explode(',', (string) $raw)));