2

I am attempting to extend the custom options on a product. I'd like to add a number of fields that would be too much to display in the same row as the rest of the custom options. They are completely optional and so I'd like to have a link in each option value that opens a modal that will display a form to collect the other values:

Custom Option Links

Using a PHP Modifier I am able to add the link to open the modal as a child of the Magento_Ui/js/dynamic-rows/record component.

'arguments' => [
    'data' => [
        'config' => [
            'title' => __('My Custom Fields'),
            'formElement' => Container::NAME,
            'componentType' => Container::NAME,
            'component' => 'Magento_Ui/js/form/components/button',
            'actions' => [
                [
                    'targetName' => 'ns=product_form, index=my_fields_modal',
                    'actionName' => 'openModal',
                ],
            ],
            'displayAsLink' => true,
            'sortOrder' => $sortOrder,
        ],
    ],
]

This opens the window and displays the form...

enter image description here

Where I am getting stuck is finding a way to tie these values back to the associated catalog_product_option_type_value record. (Either in new columns in that table or in a new table where I can reference catalog_product_option_type_value)

Because of the current lack of documentation around the new ui components I don't have a clear understanding of how that part ties in.

Is there a way to pass the current index/row when opening the modal, in order to name the form fields in such a way that they will be saved to that table?

Any help would be greatly appreciated.

3
  • After a little more tinkering, I've figured out that really what I am asking is how do I change the datascope of a modal on opening. If I hardcode the dataScope of the modal to: data.product.options.0.values.0 it shows/saves the value of the first option's first value.
    – Dan Buchal
    Nov 1, 2016 at 18:59
  • Dan Buchal can you provide the tutorial for how to add that sliding window to our custom model
    – vijay b
    Feb 2, 2017 at 11:34
  • @DanBuchal I faced same have you get any solution? magento.stackexchange.com/questions/293336/… Oct 30, 2019 at 11:01

1 Answer 1

0

I finally figured out a way to do this.

I will warn you, it's not pretty. If anyone has a better way of doing this, I'm all ears!

There has to be a better way of doing this, but this is what I was able to come up with after much trial and error... and it works in my scenario (Disclaimer: Your results may vary).

Basically, the idea here is to extend the modal with our own which has a few methods that take care of:

  • "un-doing" all of the various knockout bindings etc
  • changing the fields that need to be changed
  • and then re-applying the bindings.

The updateElement method below is recursive and also takes care of all child elements.
It specifically targets certain properties that I need (like elem.imports.visible).
You may need others for your situation.

My hope at the start of this was to find one method I just needed to call which would basically re-build everything, but unfortunately I was never able to find one.

My custom modal component:

define([
    'Magento_Ui/js/modal/modal-component',
    'jquery',
    'ko',
    'underscore'
], function (Modal, $, ko, _) {
    'use strict';

    return Modal.extend({
        /**
         * Set the dataScope, and related properties / actions
         *
         * @param {String} dataScope
         */
        setDataScope: function (dataScope) {
            this.originalDataScope = this.dataScope;
            this.newDataScope = dataScope;

            // Unbind modal content children
            var modalElement = $('.product_form_product_form_my_fields_modal');
            var modalContent = modalElement.find('.modal-content');
            modalContent.find('*').each(function () {
                $(this).unbind();
            });

            // Clean nodes
            ko.utils.arrayForEach(modalContent[0].childNodes, ko.cleanNode);

            // Update Element and Child Elements
            this.updateElement(this);

            // Re-apply ko bindings
            ko.applyBindings(this, modalContent[0]);

            // Remove modal
            delete this.modal;

            // Detach modal DOM element
            modalElement.detach();

            // Re-init modal content
            this.initializeContent();
        },

        /**
         * Update element recursively
         *
         * @param {Object} elem
         */
        updateElement: function (elem) {
            // Update datascope
            if (elem.dataScope) {
                elem.dataScope = elem.dataScope.replace(this.originalDataScope, this.newDataScope);
            }

            // Update parent datascope
            if (elem.parentScope) {
                elem.parentScope = elem.parentScope.replace(this.originalDataScope, this.newDataScope);
            }

            // Container changes
            if (elem.componentType === 'container') {
                // Re-initialize links
                if (elem.imports.visible) {
                    elem.imports.visible = elem.imports.visible.replace(this.originalDataScope, this.newDataScope);

                    elem.initLinks();
                }
            }

            // Input field changes
            if (elem.inputName) {
                // Dispose of all existing subscriptions for input field value
                while (elem.value._subscriptions.change[0]) {
                    elem.value._subscriptions.change[0].dispose();
                }

                // Remove imports & exports
                elem.maps.imports = {};
                elem.maps.exports = {};

                // Change input name
                elem.inputName = this.dataScopeToHtmlArray(elem.dataScope);

                // Re-initialize links
                if (elem.links.value) {
                    elem.links.value = elem.links.value.replace(this.originalDataScope, this.newDataScope);

                    elem.initLinks();
                }
            }

            // Recursively update all child elements
            if (_.isFunction(elem.elems)) {
                _.each(elem.elems(), this.updateElement, this);
            }
        },

        /**
         * Get HTML array from data scope.
         *
         * @param {String} dataScopeString
         * @returns {String}
         */
        dataScopeToHtmlArray: function (dataScopeString) {
            var dataScopeArray, dataScope, reduceFunction;

            /**
             * Add new level of nesting.
             *
             * @param {String} prev
             * @param {String} curr
             * @returns {String}
             */
            reduceFunction = function (prev, curr) {
                return prev + '[' + curr + ']';
            };

            dataScopeArray = dataScopeString.split('.');

            dataScope = dataScopeArray.shift();
            dataScope += dataScopeArray.reduce(reduceFunction, '');

            return dataScope;
        }
    });
});

In the definition for my modal I changed the component to be the one I created above.

Then, to call this component I then changed my PHP Modifier to call our method with the datascope for the current row:

'arguments' => [
    'data' => [
        'config' => [
            'title' => __('My Custom Fields'),
            'formElement' => Container::NAME,
            'componentType' => Container::NAME,
            'component' => 'Magento_Ui/js/form/components/button',
            'actions' => [
                [
                    'targetName' => 'ns=product_form, index=my_fields_modal',
                    'actionName' => 'setDataScope',
                    'params' => [
                        '${$.dataScope}'
                    ]
                ],
                [
                    'targetName' => 'ns=product_form, index=my_fields_modal',
                    'actionName' => 'openModal',
                ],
            ],
            'displayAsLink' => true,
            'sortOrder' => $sortOrder,
        ],
    ],
]

Note: A bug with Magento core code exists which will cause this to fail the second time the link is clicked. A description of the issue and my work-around can be found here: https://github.com/magento/magento2/issues/7445

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.