Question

On category page I want to add addition view mode as "expand" with "grid" and "list" mode.

enter image description here

If any one has created custom view mode for category page then help.

Was it helpful?

Solution

I have tried to find solution to create custom view mode on category page. But no luck :(

So, I have check core files of magento 2. From where the view mode add, and I got idea how to create new mode. Below are the steps I have performed to create module.

Step #1: Created a module registration file app/code/Darsh/Expandmode/registration.php

<?php
\Magento\Framework\Component\ComponentRegistrar::register(
        \Magento\Framework\Component\ComponentRegistrar::MODULE, 'Darsh_Expandmode', __DIR__
);

Step #2: Created a composer.json file app/code/Darsh/Expandmode/composer.json

{
    "name": "darsh/magento2-expandmode",
    "description": "Category page custom view mode",
    "version": "1.0.0",
    "require": {
        "magento/module-backend": "~100.0.0"
    },
    "type": "magento2-module",
    "extra": {
        "map": [
            [
                "*",
                "Darsh/Expandmode"
            ]
        ]
    }
}

Step #3: Created a module.xml file app/code/Darsh/Expandmode/etc/module.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
    <module name="Darsh_Expandmode" setup_version="1.0.0" >        
    </module>
</config>

Step #4: Created a di.xml file to overwrite core magento functionality. app/code/Darsh/Expandmode/etc/di.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <preference for="Magento\Catalog\Model\Config\Source\ListMode" type="Darsh\Expandmode\Model\Config\Source\ListMode" />    
    <preference for="Magento\Catalog\Helper\Product\ProductList" type="Darsh\Expandmode\Helper\Product\ProductList" />    
</config>

Step #5: Created a ListMode.php file to overwrite core magento functionality. app/code/Darsh/Expandmode/Model/Config/Source/ListMode.php

<?php
namespace Darsh\Expandmode\Model\Config\Source;

class ListMode extends \Magento\Catalog\Model\Config\Source\ListMode
{
    /**
     * {@inheritdoc}
     *
     * @codeCoverageIgnore
     */
    public function toOptionArray()
    {
        return [
            ['value' => 'grid', 'label' => __('Grid Only')],
            ['value' => 'list', 'label' => __('List Only')],
            ['value' => 'grid-list', 'label' => __('Grid (default) / List')],
            ['value' => 'list-grid', 'label' => __('List (default) / Grid')],
            ['value' => 'list-grid-expand', 'label' => __('Expand (default) / List / Grid')]
        ];
    }

}

Step #6: Created a ProductList.php file to overwrite core magento functionality. app/code/Darsh/Expandmode/Helper/Product/ProductList.php

<?php
namespace Darsh\Expandmode\Helper\Product;

class ProductList extends \Magento\Catalog\Helper\Product\ProductList
{
     /**
     * List mode configuration path
     */
    const XML_PATH_LIST_MODE = 'catalog/frontend/list_mode';

    const VIEW_MODE_LIST = 'list';
    const VIEW_MODE_GRID = 'grid';    

    const DEFAULT_SORT_DIRECTION = 'asc';
    /**
     * @var \Magento\Framework\App\Config\ScopeConfigInterface
     */
    protected $scopeConfig;

    /**
     * @var \Magento\Framework\Registry
     */
    private $coreRegistry;

    /**
     * Default limits per page
     *
     * @var array
     */
    protected $_defaultAvailableLimit  = [10 => 10,20 => 20,50 => 50];

    /**
     * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
     */
    public function __construct(
        \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
        \Magento\Framework\Registry $coreRegistry = null
    ) {
        $this->scopeConfig = $scopeConfig;
        $this->coreRegistry = $coreRegistry ?: \Magento\Framework\App\ObjectManager::getInstance()->get(
            \Magento\Framework\Registry::class
        );
    }

    /**
     * Returns available mode for view
     *
     * @return array|null
     */
    public function getAvailableViewMode()
    {
        $value = $this->scopeConfig->getValue(
            self::XML_PATH_LIST_MODE,
            \Magento\Store\Model\ScopeInterface::SCOPE_STORE
        );
        switch ($value) {
            case 'grid':
                $availableMode = ['grid' => __('Grid')];
                break;

            case 'list':
                $availableMode = ['list' => __('List')];
                break;

            case 'grid-list':
                $availableMode = ['grid' => __('Grid'), 'list' =>  __('List')];
                break;

            case 'list-grid':
                $availableMode = ['list' => __('List'), 'grid' => __('Grid')];
                break;

            case 'list-grid-expand':
                $availableMode = ['expand' => __('Expand'), 'list' => __('List'), 'grid' => __('Grid')];
                break;
            default:
                $availableMode = null;
                break;
        }
        return $availableMode;
    }

    /**
     * Returns default view mode
     *
     * @param array $options
     * @return string
     */
    public function getDefaultViewMode($options = [])
    {
        if (empty($options)) {
            $options = $this->getAvailableViewMode();
        }
        return current(array_keys($options));
    }

    /**
     * Get default sort field
     *
     * @return null|string
     */
    public function getDefaultSortField()
    {
        $currentCategory = $this->coreRegistry->registry('current_category');
        if ($currentCategory) {
            return $currentCategory->getDefaultSortBy();
        }

        return $this->scopeConfig->getValue(
            \Magento\Catalog\Model\Config::XML_PATH_LIST_DEFAULT_SORT_BY,
            \Magento\Store\Model\ScopeInterface::SCOPE_STORE
        );
    }

    /**
     * Retrieve available limits for specified view mode
     *
     * @param string $mode
     * @return array
     */
    public function getAvailableLimit($mode)
    {
        if (!in_array($mode, [self::VIEW_MODE_GRID, self::VIEW_MODE_LIST])) {
            return $this->_defaultAvailableLimit;
        }
        $perPageConfigKey = 'catalog/frontend/' . $mode . '_per_page_values';
        $perPageValues = (string)$this->scopeConfig->getValue(
            $perPageConfigKey,
            \Magento\Store\Model\ScopeInterface::SCOPE_STORE
        );
        $perPageValues = explode(',', $perPageValues);
        $perPageValues = array_combine($perPageValues, $perPageValues);
        if ($this->scopeConfig->isSetFlag(
            'catalog/frontend/list_allow_all',
            \Magento\Store\Model\ScopeInterface::SCOPE_STORE
        )) {
            return ($perPageValues + ['all' => __('All')]);
        } else {
            return $perPageValues;
        }
    }

    /**
     * Retrieve default per page values
     *
     * @param string $viewMode
     * @return string (comma separated)
     */
    public function getDefaultLimitPerPageValue($viewMode)
    {
        if ($viewMode == self::VIEW_MODE_LIST) {
            return $this->scopeConfig->getValue(
                'catalog/frontend/list_per_page',
                \Magento\Store\Model\ScopeInterface::SCOPE_STORE
            );
        } elseif ($viewMode == self::VIEW_MODE_GRID) {
            return $this->scopeConfig->getValue(
                'catalog/frontend/grid_per_page',
                \Magento\Store\Model\ScopeInterface::SCOPE_STORE
            );
        }
        return 0;
    }

}

Step #7: Applied changes in layout section app/code/Darsh/Expandmode/view/frontend/layout/catalog_category_view.xml

<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="2columns-left" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceBlock name="category.products.list" template="Darsh_Expandmode::product/list.phtml" />
    </body>
</page>

Step #8: Applied changes in layout section app/code/Darsh/Expandmode/view/frontend/templates/product/list.phtml

<?php
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
use Magento\Framework\App\Action\Action;

// @codingStandardsIgnoreFile

?>
<?php
/**
 * Product list template
 *
 * @var $block \Magento\Catalog\Block\Product\ListProduct
 */
?>
<?php
$_productCollection = $block->getLoadedProductCollection();
$_helper = $this->helper('Magento\Catalog\Helper\Output');
?>
<?php if (!$_productCollection->count()): ?>
    <div class="message info empty"><div><?= /* @escapeNotVerified */ __('We can\'t find products matching the selection.') ?></div></div>
<?php else: ?>
    <?= $block->getToolbarHtml() ?>
    <?= $block->getAdditionalHtml() ?>
    <?php
    if ($block->getMode() == 'grid') {
        $viewMode = 'grid';
        $imageDisplayArea = 'category_page_grid';
        $showDescription = false;
        $templateType = \Magento\Catalog\Block\Product\ReviewRendererInterface::SHORT_VIEW;
    } elseif ($block->getMode() == 'expand') {
        $viewMode = 'expand';
        $imageDisplayArea = 'category_page_list';
        $showDescription = true;
        $templateType = \Magento\Catalog\Block\Product\ReviewRendererInterface::FULL_VIEW;
    }else{
        $viewMode = 'list';
        $imageDisplayArea = 'category_page_list';
        $showDescription = true;
        $templateType = \Magento\Catalog\Block\Product\ReviewRendererInterface::FULL_VIEW;
    }
    /**
     * Position for actions regarding image size changing in vde if needed
     */
    $pos = $block->getPositioned();
    ?>
    <div class="products wrapper <?= /* @escapeNotVerified */ $viewMode ?> products-<?= /* @escapeNotVerified */ $viewMode ?>">
        <ol class="products list items product-items">
            <?php /** @var $_product \Magento\Catalog\Model\Product */ ?>
            <?php foreach ($_productCollection as $_product): ?>
            <li class="item product product-item">
                <div class="product-item-info" data-container="product-<?= /* @escapeNotVerified */ $viewMode ?>">
                    <?php
                    $productImage = $block->getImage($_product, $imageDisplayArea);
                    if ($pos != null) {
                        $position = ' style="left:' . $productImage->getWidth() . 'px;'
                            . 'top:' . $productImage->getHeight() . 'px;"';
                    }
                    ?>
                    <?php // Product Image ?>
                    <a href="<?= /* @escapeNotVerified */ $_product->getProductUrl() ?>" class="product photo product-item-photo" tabindex="-1">
                        <?= $productImage->toHtml() ?>
                    </a>
                    <div class="product details product-item-details">
                        <?php
                            $_productNameStripped = $block->stripTags($_product->getName(), null, true);
                        ?>
                        <strong class="product name product-item-name">
                            <a class="product-item-link"
                               href="<?= /* @escapeNotVerified */ $_product->getProductUrl() ?>">
                                <?= /* @escapeNotVerified */ $_helper->productAttribute($_product, $_product->getName(), 'name') ?>
                            </a>
                        </strong>
                        <?= $block->getReviewsSummaryHtml($_product, $templateType) ?>
                        <?= /* @escapeNotVerified */ $block->getProductPrice($_product) ?>
                        <?= $block->getProductDetailsHtml($_product) ?>

                        <div class="product-item-inner">
                            <div class="product actions product-item-actions"<?= strpos($pos, $viewMode . '-actions') ? $position : '' ?>>
                                <div class="actions-primary"<?= strpos($pos, $viewMode . '-primary') ? $position : '' ?>>
                                    <?php if ($_product->isSaleable()): ?>
                                        <?php $postParams = $block->getAddToCartPostParams($_product); ?>
                                        <form data-role="tocart-form" data-product-sku="<?= $block->escapeHtml($_product->getSku()) ?>" action="<?= /* @NoEscape */ $postParams['action'] ?>" method="post">
                                            <input type="hidden" name="product" value="<?= /* @escapeNotVerified */ $postParams['data']['product'] ?>">
                                            <input type="hidden" name="<?= /* @escapeNotVerified */ Action::PARAM_NAME_URL_ENCODED ?>" value="<?= /* @escapeNotVerified */ $postParams['data'][Action::PARAM_NAME_URL_ENCODED] ?>">
                                            <?= $block->getBlockHtml('formkey') ?>
                                            <button type="submit"
                                                    title="<?= $block->escapeHtml(__('Add to Cart')) ?>"
                                                    class="action tocart primary">
                                                <span><?= /* @escapeNotVerified */ __('Add to Cart') ?></span>
                                            </button>
                                        </form>
                                    <?php else: ?>
                                        <?php if ($_product->isAvailable()): ?>
                                            <div class="stock available"><span><?= /* @escapeNotVerified */ __('In stock') ?></span></div>
                                        <?php else: ?>
                                            <div class="stock unavailable"><span><?= /* @escapeNotVerified */ __('Out of stock') ?></span></div>
                                        <?php endif; ?>
                                    <?php endif; ?>
                                </div>
                                <div data-role="add-to-links" class="actions-secondary"<?= strpos($pos, $viewMode . '-secondary') ? $position : '' ?>>
                                    <?php if ($addToBlock = $block->getChildBlock('addto')): ?>
                                        <?= $addToBlock->setProduct($_product)->getChildHtml() ?>
                                    <?php endif; ?>
                                </div>
                            </div>
                            <?php if ($showDescription):?>
                                <div class="product description product-item-description">
                                    <?= /* @escapeNotVerified */ $_helper->productAttribute($_product, $_product->getShortDescription(), 'short_description') ?>
                                    <a href="<?= /* @escapeNotVerified */ $_product->getProductUrl() ?>" title="<?= /* @escapeNotVerified */ $_productNameStripped ?>"
                                       class="action more"><?= /* @escapeNotVerified */ __('Learn More') ?></a>
                                </div>
                            <?php endif; ?>
                        </div>
                    </div>
                </div>
            </li>
            <?php endforeach; ?>
        </ol>
    </div>
    <?= $block->getToolbarHtml() ?>
    <?php if (!$block->isRedirectToCartEnabled()) : ?>
        <script type="text/x-magento-init">
        {
            "[data-role=tocart-form], .form.map.checkout": {
                "catalogAddToCart": {
                    "product_sku": "<?= /* @NoEscape */ $_product->getSku() ?>"
                }
            }
        }
        </script>
    <?php endif; ?>
<?php endif; ?>

As per my requirment I have made changes on above files, but basic idea you will get to create new custom view mode on category page.

Thanks :)

Licensed under: CC-BY-SA with attribution
Not affiliated with magento.stackexchange
scroll top