Friday, July 7, 2017

How to use Rendere with HTML conent in UI Component Grid in Magento 2

In my custom module grid i have to use custom grid column which render a field and create custom html content how to implement it in Magento 2 1. in ui listing xml add below code in side columns tag
<column name="manufacturers" class="NAMEPACE\MODULENAME\Ui\Component\Listing\Column\Product\Manufacturers">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="component" xsi:type="string">NAMEPACE_MODULENAME/js/columns/html</item>
                    <item name="sortable" xsi:type="boolean">false</item>
                    <item name="altField" xsi:type="string">name</item>
                    <item name="label" xsi:type="string" translate="true">Manufacturers</item>
                    <item name="sortOrder" xsi:type="number">90</item>
                </item>
            </argument>
        </column>

2. Create Column file (path as per class name)

<?php
/**
 * Copyright © 2013-2017 Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
namespace NAMEPACE\MODULENAME\Ui\Component\Listing\Column\Product;

use Magento\Framework\View\Element\UiComponentFactory;
use Magento\Framework\View\Element\UiComponent\ContextInterface;

class Manufacturers extends \Magento\Ui\Component\Listing\Columns\Column
{
    /**
     * Column name
     */
    const NAME = 'column.manufacturers';
    /**
     * @param ContextInterface $context
     * @param UiComponentFactory $uiComponentFactory
     * @param \Magento\Framework\Locale\CurrencyInterface $localeCurrency
     * @param \Magento\Store\Model\StoreManagerInterface $storeManager
     * @param array $components
     * @param array $data
     */
    public function __construct(
        ContextInterface $context,
        UiComponentFactory $uiComponentFactory,
        array $components = [],
        array $data = []
    ) {
        parent::__construct($context, $uiComponentFactory, $components, $data);
    }

    /**
     * Prepare Data Source
     *
     * @param array $dataSource
     * @return array
     */
    public function prepareDataSource(array $dataSource)
    {
        if (isset($dataSource['data']['items'])) {

            $fieldName = $this->getData('name');
            foreach ($dataSource['data']['items'] as & $item) {
                if (isset($item[$fieldName])) {
                    $html = '';
                    $manufacturers = unserialize($item[$fieldName]);
                    if (!empty($manufacturers)) {
                        foreach ($manufacturers as $manufacturer) {
                            if ($manufacturer['primary'] == 'Y') {
                                $html .= '<strong>';
                            }

                            $html .= $manufacturer['name'];

                            if (!empty($manufacturer['product_code'])) {
                                $html .= ' | ' . __('SKU') . ': ' . $manufacturer['product_code'];
                            }

                            if ($manufacturer['primary'] == 'Y') {
                                $html .= '</strong>';
                            }

                            $html .= '<br />';
                        }
                    }
                    $item[$fieldName] = $html;
                }
            }
        }

        return $dataSource;
    }
}
3. create custom js file to reneder html conent path :- NAMESPACE/MODULENAME/view/adminhtml/web/js/columns/html.js
/**
 * Copyright © 2013-2017 Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
define([
    'underscore',
    'uiRegistry',
    'mageUtils',
    'uiElement'
], function (_, registry, utils, Element) {
    'use strict';

    return Element.extend({
        defaults: {
            headerTmpl: 'ui/grid/columns/text',
            bodyTmpl: 'ui/grid/cells/html',
            disableAction: false,
            controlVisibility: true,
            sortable: true,
            sorting: false,
            visible: true,
            draggable: true,
            fieldClass: {},
            ignoreTmpls: {
                fieldAction: true
            },
            statefull: {
                visible: true,
                sorting: true
            },
            imports: {
                exportSorting: 'sorting'
            },
            listens: {
                '${ $.provider }:params.sorting.field': 'onSortChange'
            },
            modules: {
                source: '${ $.provider }'
            }
        },

        /**
         * Initializes column component.
         *
         * @returns {Column} Chainable.
         */
        initialize: function () {
            this._super()
                .initFieldClass();

            return this;
        },

        /**
         * Initializes observable properties.
         *
         * @returns {Column} Chainable.
         */
        initObservable: function () {
            this._super()
                .track([
                    'visible',
                    'sorting',
                    'disableAction'
                ])
                .observe([
                    'dragging'
                ]);

            return this;
        },

        /**
         * Extends list of field classes.
         *
         * @returns {Column} Chainable.
         */
        initFieldClass: function () {
            _.extend(this.fieldClass, {
                _dragging: this.dragging
            });

            return this;
        },

        /**
         * Applies specified stored state of a column or one of its' properties.
         *
         * @param {String} state - Defines what state should be used: saved or default.
         * @param {String} [property] - Defines what columns' property should be applied.
         *      If not specified, then all columns stored properties will be used.
         * @returns {Column} Chainable.
         */
        applyState: function (state, property) {
            var namespace = this.storageConfig.root;

            if (property) {
                namespace += '.' + property;
            }

            this.storage('applyStateOf', state, namespace);

            return this;
        },

        /**
         * Sets columns' sorting. If column is currently sorted,
         * than its' direction will be toggled.
         *
         * @param {*} [enable=true] - If false, than sorting will
         *      be removed from a column.
         * @returns {Column} Chainable.
         */
        sort: function (enable) {
            if (!this.sortable) {
                return this;
            }

            enable !== false ?
                this.toggleSorting() :
                this.sorting = false;

            return this;
        },

        /**
         * Sets descending columns' sorting.
         *
         * @returns {Column} Chainable.
         */
        sortDescending: function () {
            if (this.sortable) {
                this.sorting = 'desc';
            }

            return this;
        },

        /**
         * Sets ascending columns' sorting.
         *
         * @returns {Column} Chainable.
         */
        sortAscending: function () {
            if (this.sortable) {
                this.sorting = 'asc';
            }

            return this;
        },

        /**
         * Toggles sorting direction.
         *
         * @returns {Column} Chainable.
         */
        toggleSorting: function () {
            this.sorting === 'asc' ?
                this.sortDescending() :
                this.sortAscending();

            return this;
        },

        /**
         * Checks if column is sorted.
         *
         * @returns {Boolean}
         */
        isSorted: function () {
            return !!this.sorting;
        },

        /**
         * Exports sorting data to the dataProvider if
         * sorting of a column is enabled.
         */
        exportSorting: function () {
            if (!this.sorting) {
                return;
            }

            this.source('set', 'params.sorting', {
                field: this.index,
                direction: this.sorting
            });
        },

        /**
         * Checks if column has an assigned action that will
         * be performed when clicking on one of its' fields.
         *
         * @returns {Boolean}
         */
        hasFieldAction: function () {
            return !!this.fieldAction || !!this.fieldActions;
        },

        /**
         * Applies action described in a 'fieldAction' property
         * or actions described in 'fieldActions' property.
         *
         * @param {Number} rowIndex - Index of a row which initiates action.
         * @returns {Column} Chainable.
         *
         * @example Example of fieldAction definition, which is equivalent to
         *      referencing to external component named 'listing.multiselect'
         *      and calling its' method 'toggleSelect' with params [rowIndex, true] =>
         *
         *      {
         *          provider: 'listing.multiselect',
         *          target: 'toggleSelect',
         *          params: ['${ $.$data.rowIndex }', true]
         *      }
         */
        applyFieldAction: function (rowIndex) {
            if (!this.hasFieldAction() || this.disableAction) {
                return this;
            }

            if (this.fieldActions) {
                this.fieldActions.forEach(this.applySingleAction.bind(this, rowIndex), this);
            } else {
                this.applySingleAction(rowIndex);
            }

            return this;
        },

        /**
         * Applies single action
         *
         * @param {Number} rowIndex - Index of a row which initiates action.
         * @param {Object} action - Action (fieldAction) to be applied
         *
         */
        applySingleAction: function (rowIndex, action) {
            var callback;

            action = action || this.fieldAction;
            action = utils.template(action, {
                column: this,
                rowIndex: rowIndex
            }, true);

            callback = this._getFieldCallback(action);

            if (_.isFunction(callback)) {
                callback();
            }
        },

        /**
         * Returns field action handler if it was specified.
         *
         * @param {Object} record - Record object with which action is associated.
         * @returns {Function|Undefined}
         */
        getFieldHandler: function (record) {
            if (this.hasFieldAction()) {
                return this.applyFieldAction.bind(this, record._rowIndex);
            }
        },

        /**
         * Creates action callback based on its' data.
         *
         * @param {Object} action - Actions' object.
         * @returns {Function|Boolean} Callback function or false
         *      value if it was impossible create a callback.
         */
        _getFieldCallback: function (action) {
            var args     = action.params || [],
                callback = action.target;

            if (action.provider && action.target) {
                args.unshift(action.target);

                callback = registry.async(action.provider);
            }

            if (!_.isFunction(callback)) {
                return false;
            }

            return function () {
                callback.apply(callback, args);
            };
        },

        /**
         * Ment to preprocess data associated with a current columns' field.
         *
         * @param {Object} record - Data to be preprocessed.
         * @returns {String}
         */
        getLabel: function (record) {
            return record[this.index];
        },

        /**
         * Returns list of classes that should be applied to a field.
         *
         * @returns {Object}
         */
        getFieldClass: function () {
            return this.fieldClass;
        },

        /**
         * Returns path to the columns' header template.
         *
         * @returns {String}
         */
        getHeader: function () {
            return this.headerTmpl;
        },

        /**
         * Returns path to the columns' body template.
         *
         * @returns {String}
         */
        getBody: function () {
            return this.bodyTmpl;
        },

        /**
         * Listener of the providers' sorting state changes.
         *
         * @param {Srting} field - Field by which current sorting is performed.
         */
        onSortChange: function (field) {
            if (field !== this.index) {
                this.sort(false);
            }
        }
    });
});
Clear cache :-)