sbodak/magento2-checkout-custom-form

Dropdown instead of input field

C4rter opened this issue · 7 comments

I really love your extension. It sheds some light in adding new fields to the checkout process.
Compared to Magento 1 this is a lot of pain. I don't understand why they had to make it much more complex to add new fields.

Do you know how a drop down instead of input field would work?
I know that "ui/form/element/select" shows me a drop down. But I can't figure out how to add options to this.

I got it working with defining the values in the XML according to the Magento example:
http://devdocs.magento.com/guides/v2.0/howdoi/checkout/checkout_form.html

But how would I get them from a model?

@C4rter

0. To add select input with option values you need to add following code and probably you found it already:

/* view/frontend/layout/checkout_index_index.xml */
<item name="select_field" xsi:type="array">
	<item name="component" xsi:type="string">Magento_Ui/js/form/element/select</item>
	<item name="config" xsi:type="array">
		<!--customScope is used to group elements within a single form (e.g. they can be validated separately)-->
		<item name="customScope" xsi:type="string">customCheckoutForm</item>
		<item name="template" xsi:type="string">ui/form/field</item>
		<item name="elementTmpl" xsi:type="string">ui/form/element/select</item>
	</item>
	<item name="options" xsi:type="array">
		<item name="0" xsi:type="array">
			<item name="label" xsi:type="string">Please select value</item>
			<item name="value" xsi:type="string"></item>
		</item>
		<item name="1" xsi:type="array">
			<item name="label" xsi:type="string">Option 1</item>
			<item name="value" xsi:type="string">value_1</item>
		</item>
		<item name="2" xsi:type="array">
			<item name="label" xsi:type="string">Option 2</item>
			<item name="value" xsi:type="string">value_2</item>
		</item>
	</item>
	<!-- value element allows to specify default value of the form field -->
	<item name="value" xsi:type="string">value_2</item>
	<item name="provider" xsi:type="string">checkoutProvider</item>
	<item name="dataScope" xsi:type="string">customCheckoutForm.select_field</item>
	<item name="label" xsi:type="string">Select Field</item>
	<item name="sortOrder" xsi:type="string">2</item>
</item>

You need to add few more things as i described in README.md file.
Be aware that in above example option name is select_field.
In a real case, you should name it e.g as new_name_select.

What next?
1) Add declaration of new field to Api/Data/CustomFieldsInterfaces.php. This is service contract data interface.

const CHECKOUT_NEW_NAME_SELECT = 'new_name_select';
// add here also setter and getter
/**
 * Get new select name
 *
 * @return string|null
 */
public function getNewNameSelect();

/**
 * Set new select name
 *
 * @param string|null $newNameSelect New Name Select
 *
 * @return CustomFieldsInterface
 */
public function setNewNameSelect(string $newNameSelect = null);

2) Implement new field logic:

// Model/Data/CustomFields.php
/**
 * Get new select name
 *
 * @return string|null
 */
public function getNewNameSelect()
{
	return $this->_get(self::CHECKOUT_NEW_NAME_SELECT );
}

/**
 * Set new select name
 *
 * @param string|null $newNameSelect New Name Select
 *
 * @return CustomFieldsInterface
 */
public function setNewNameSelect(string $newNameSelect = null)
{
	return $this->setData(self::CHECKOUT_NEW_NAME_SELECT , $newNameSelect);
}

3. Add a new field to your DB (to quota and order tables)

// Setup/InstallData.php
// in installQuoteData() method
 $quoteInstaller
	->addAttribute(
		'quote',
		CustomFieldsInterface::CHECKOUT_NEW_NAME_SELECT,
		['type' => Table::TYPE_TEXT, 'length' => '255', 'nullable' => true]
	);

// in installSalesData() method
	$salesInstaller
            ->addAttribute(
                'order',
                CustomFieldsInterface::CHECKOUT_NEW_NAME_SELECT,
                ['type' => Table::TYPE_TEXT, 'length' => '255', 'nullable' => true, 'grid' => false]
            );

4. Save new field value in quota

//Model/CustomFieldsRepository.php
$cart->setData(
	CustomFieldsInterface::CHECKOUT_NEW_NAME_SELECT,
	$customFields->getNewNameSelect()
);

5. Copy and save new field value to order

// Observer/AddCustomFieldsToOrder.php
$order->setData(
	CustomFieldsInterface::CHECKOUT_NEW_NAME_SELECT,
	$quote->getData(CustomFieldsInterface::CHECKOUT_NEW_NAME_SELECT)
);

6. Update frontend view for customer account

/* view/frontend/templates/order/view/custom_fields.phtml */
<strong><?php /* @escapeNotVerified */ echo __('New select name') ?>:</strong>
<?php echo $this->escapeHtml($customFields->getNewNameSelect()); ?><br>

7. Update frontend view for order details in admin panel

/* view/adminhtml/templates/order/view/custom_fields.phtml */
<div class="box">
	<strong class="box-title"><span><?php /* @escapeNotVerified */ echo __('New select name') ?></span></strong>
	<div class="box-content">
		<?php echo $this->escapeHtml($customFields->getNewNameSelect()); ?>
	</div>
</div>

@C4rter Do you have more question or can I Close this issue?

@sbodak Yes I got it working with your example and the Magento description.
Thank you for your detailed answer.

I came across another thing:
How do you control where the additional fields appear?
I tried experimenting with different elements and positioning in the layout xml but it did not work very well. I managed to move it to the billing step for example but smaller changes did not work.

I wanted to place the fields below the shipping-address instead of above. I noticed this in your file:

<item name="shippingAddress" xsi:type="array">
<item name="children" xsi:type="array">
<item name="before-form" xsi:type="array">

And I tried "after-form" instead, but it did not work. I guess that's just a name and has nothing to do with placement?

You can check available default items in:
/vendor/magento/module-checkout/view/frontend/layout/checkout_index_index.xml

You can see there that before-form is name of item.

<item name="shippingAddress" xsi:type="array">
	...
   
	<item name="children" xsi:type="array">
		<item name="customer-email" xsi:type="array">
			...
		</item>
		<item name="before-form" xsi:type="array">
		   ...
		</item>
		<item name="before-fields" xsi:type="array">
		   ...
		</item>
		<item name="address-list" xsi:type="array">
			...
		</item>
		<item name="address-list-additional-addresses" xsi:type="array">
		  ...
		</item>
		<item name="before-shipping-method-form" xsi:type="array">
		  ...
		</item>
		<item name="shipping-address-fieldset" xsi:type="array">
			...
		</item>
	</item>
</item>

Custom form is now set by default after Shipping address form.
custom_form_position

@C4rter Let me know if everything works correctly.

@sbodak is there any "after-shipping-method-form"? I was trying to show block after shipment method list but its not showing however "before-shipping-method-form" works well.
And also want to show/hide this block after selection of particular shipment method.
Please help