Thursday, October 19, 2017

Magento 2 - How to keep those checkout error messages from disappearing

If you ever had an error during checkout, you might have noticed that the error message hides after a few seconds. It's five seconds to be exact. That's too quick for me. Many times I don't notice it until it disappears.

Why hide it at all? I think it's best to keep it visible until the customer decides to hide it. Here's how to accomplish that:

First, copy this file:
vendor/magento/module-ui/view/frontend/web/template/messages.html

Over to:
app/design/frontend/YourPackage/YourTheme/Magento_Ui/web/template/messages.html

Then change this line:
<div data-role="checkout-messages" class="messages" data-bind="visible: isVisible(), click: removeAll">

To:
<div data-role="checkout-messages" class="messages" data-bind="click: removeAll">

Clear your cache and re-deploy the static content:
bin/magento setup:upgrade
bin/magento setup:static-content:deploy

There you have it. This will keep the message visible until the customer clicks on it.

How does this work?
The "visible: isVisible()" part tells the component to trigger a timer when the error message becomes visible. You can see this in:
vendor/magento/module-ui/view/frontend/web/js/view/messages.js
/**
 * Checks visibility.
 *
 * @return {Boolean}
 */
isVisible: function () {
    return this.isHidden(this.messageContainer.hasMessages());
},
defaults: {
    template: 'Magento_Ui/messages',
    selector: '[data-role=checkout-messages]',
    isHidden: false,
    listens: {
        isHidden: 'onHiddenChange'
    }
},
This watches for any error messages that might load, and to call onHiddenChange() when that happens:
onHiddenChange: function (isHidden) {
    var self = this;

    // Hide message block if needed
    if (isHidden) {
        setTimeout(function () {
            $(self.selector).hide('blind', {}, 500);
        }, 5000);
    }
}
This tells it to hide the error message after 5000 milliseconds.

The choice to use "isHidden" as the variable name is a bit confusing. When the message container has messages, isHidden is set to true. That sounds backwards to me. Basically, isHidden acts like a switch. It's initially set to false by default, and then gets set to true when there are messages, triggering the hide timer in onHiddenChange.

Sunday, October 15, 2017

Where is the var/generation folder in Magento 2.2.0?

I noticed a change in Magento 2.2.0. The var/generation folder is nowhere to be found. Turns out that the Magento team moved it out of the var folder for security reasons.

It's now under the root folder, and the name is changed to "generated".

Saturday, October 14, 2017

Magento 2 - How to get custom attribute value without calling Product->load() first

If you'd like to access a custom attribute, but don't want to call the resource-intensive Product::load() method beforehand, you can grab the value with the method below:
Magento\Catalog\Model\ResourceModel\AbstractResource::getAttributeRawValue($product_id, $attribute_code, $store_id)

Example:
$value = $product->getResource()->getAttributeRawValue($product->getId(), 'attribute_code', $product->getStoreId());
This returns the raw value, so for a multi-select you'll get a numeric value, like 52. To turn that into the text value, use:
$text = $product->getResource()->getAttribute('attribute_code')->getSource()->getOptionText($value);
Example with Magento 2.2.0 Sample Data:
// For this example, I am using Product ID 1 ("Joust Duffle Bag") and set the bag color to orange in the admin.

$value = $product->getResource()->getAttributeRawValue($product->getId(), 'color', $product->getStoreId());
$text = $product->getResource()->getAttribute('color')->getSource()->getOptionText($value);

echo "Color     : ".$value."\n"; // outputs '56'
echo "Color     : ".$text."\n";  // outputs 'Orange'

// Alternatively, once you have the value, you can set it and call getAttributeText() for the text value

$product->setColor($value);
echo "Color     : ".$product->getAttributeText('color')."\n"; // outputs 'Orange'