classyllama / ClassyLlama_AvaTax

This extension has been deprecated in favor of https://github.com/avadev/Avalara-AvaTax-for-Magento2
Open Software License 3.0
23 stars 15 forks source link

Cross Border Type Searchable Select doesn't allow for empty values #156

Closed LordZardeck closed 6 years ago

LordZardeck commented 6 years ago

When trying to copy the searchable select component to the product form, it was discovered that the native UI Select component doesn't allow for empty values, due to the private parseOptions function in magento/module-ui/view/base/web/js/form/element/ui-select.js. In order to even get an empty value to show up, we extended the ui-select component as seen below, but the empty value still was not sent to the server to be saved. Documenting this here in case somebody in the future can tackle this and get it working

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

/**
 * @api
 */
define([
    'Magento_Ui/js/form/element/abstract',
    'Magento_Catalog/js/components/attribute-set-select',
    'underscore'
], function (Abstract, uiSelect, _) {
    'use strict';

    /**
     * Processing options list
     *
     * @param {Array} array - Property array
     * @param {String} separator - Level separator
     * @param {Array} created - list to add new options
     *
     * @return {Array} Plain options list
     */
    function flattenCollection(array, separator, created) {
        var i = 0,
            length,
            childCollection;

        array = _.compact(array);
        length = array.length;
        created = created || [];

        for (i; i < length; i++) {
            created.push(array[i]);

            if (array[i].hasOwnProperty(separator)) {
                childCollection = array[i][separator];
                delete array[i][separator];
                flattenCollection.call(this, childCollection, separator, created);
            }
        }

        return created;
    }

    /**
     * Set levels to options list
     *
     * @param {Array} array - Property array
     * @param {String} separator - Level separator
     * @param {Number} level - Starting level
     * @param {String} path - path to root
     *
     * @returns {Array} Array with levels
     */
    function setProperty(array, separator, level, path) {
        var i = 0,
            length,
            nextLevel,
            nextPath;

        array = _.compact(array);
        length = array.length;
        level = level || 0;
        path = path || '';

        for (i; i < length; i++) {
            if (array[i]) {
                _.extend(array[i], {
                    level: level,
                    path: path
                });
            }

            if (array[i].hasOwnProperty(separator)) {
                nextLevel = level + 1;
                nextPath = path ? path + '.' + array[i].label : array[i].label;
                setProperty.call(this, array[i][separator], separator, nextLevel, nextPath);
            }
        }

        return array;
    }

    /**
     * Preprocessing options list
     *
     * @param {Array} nodes - Options list
     *
     * @return {Object} Object with property - options(options list)
     *      and cache options with plain and tree list
     */
    function parseOptions(nodes) {
        nodes = setProperty(nodes, 'optgroup');

        var compactNodes = _.compact(nodes);

        return {
            options: compactNodes,
            cacheOptions: {
                plain: _.compact(flattenCollection(JSON.parse(JSON.stringify(nodes)), 'optgroup')),
                tree: compactNodes
            }
        };
    }

    return uiSelect.extend({
        initConfig: function initConfig(config) {
            var result = parseOptions(config.options),
                defaults = this.constructor.defaults,
                multiple = _.isBoolean(config.multiple) ? config.multiple : defaults.multiple,
                type = config.selectType || defaults.selectType,
                showOpenLevelsActionIcon = _.isBoolean(config.showOpenLevelsActionIcon) ?
                    config.showOpenLevelsActionIcon :
                    defaults.showOpenLevelsActionIcon,
                openLevelsAction = _.isBoolean(config.openLevelsAction) ?
                    config.openLevelsAction :
                    defaults.openLevelsAction;

            multiple = !multiple ? 'single' : false;
            config.showOpenLevelsActionIcon = showOpenLevelsActionIcon && openLevelsAction;
            _.extend(config, result, defaults.presets[multiple], defaults.presets[type]);

            // Bypass calling our direct parent
            Abstract.prototype.initConfig.apply(this, arguments);

            return this;
        }
    });
});