0

I have the following script. Inside my initBtn function, I'm trying to call my other function populateVars, but it's erroring out (function not defined). What's the proper way of calling that function?

Summary of what the script does:

  • Initiate a zoid widget
  • Craete a hidden bootstrap modal
  • Look for elements with the class of xyzBtnHolder and populate them with a link and other HTML, so when they are clicked, the modal window opens
  • Once the modal window is opened, load an inframe inside the loaded modal
  • Also, after the modal window is opened, pass some variables into the zoid widget
  • Depending on what page they buttons are rendered on, the variables can be different
(() => {
    "use strict";
    if(window.ABCD) return;

    window.ABCD = {
        config: {
            endpoint_url: "https://example.com/embed?q=123",
            tag: 'xyz-widget',
        },

        init: function() {
            let widget = this.createZoidWidget();
            let modal = this.createModal();
            let btn = this.initBtn(modal);
        },

        createZoidWidget: function() {
            window.MYWidget = zoid.create({
                tag: ABCD.config.tag,
                url: ABCD.config.endpoint_url,
                dimensions: {
                    width: '100%',
                    height: '1000px'
                },
                props: {
                    vehicle: {
                        type: 'string'
                    },
                    vehicleType: {
                        type: 'string'
                    },
                    onSubmit: {
                        type: "function",
                        required: true,
                    },
                },
                prerenderTemplate: function containerTemplate({ doc }) {
                    const html = doc.createElement('html');
                    const p = doc.createElement('h2');
                    p.innerText = 'Please wait while the component loads...';
                    p.style.textAlign = 'center';
                    p.style.margin = '2em';
                    html.appendChild(p);
                    return html;
                },
            });
        },

        createModal: function() {
            let xyzModal = document.createElement("div");
            xyzModal.innerHTML = `
                <style>
                #xyzModal .vertical-align {display: flex; align-items: center;}
                </style>
                <div id="xyzModal" class="modal fade" tabindex="-1" role="dialog">
                    <div id="xyz-overlay"></div>
                    <div class="modal-dialog modal-lg" role="document" style="overflow-y: initial;">
                    <div class="modal-content" style="height:auto; overflow-y: scroll">
                        <div class="modal-body" style="height:80vh; overflow-y:auto;">
                            <div id="xyz-spinner-container">
                                <div id="xyz-spinner">
                                </div>
                            </div>
                            <div id="xyzFrameHolder"></div>
                        </div>
                    </div><!-- /.modal-content -->
                    </div><!-- /.modal-dialog -->
                </div><!-- /.modal -->
            `;
            $("body").append(xyzModal);
            return $(xyzModal);
        },

        initBtn: function(modal) {
            var btnHtml = `
                <style>
                    .myBtn {display: inline-block;}
                </style>
                <a data-toggle="modal" data-target="#xyzModal" href="#" class="myBtn"></a>
            `;

            $(".xyzBtnHolder").css({'maxWidth' : '260px'});
            document.querySelector('.xyzBtnHolder').insertAdjacentHTML('beforeend', btnHtml);

            $(document).on('shown.bs.modal', '#xyzModal', function (event) {
                var triggerElement = $(event.relatedTarget);
                $(".modal-backdrop.in").css({opacity: 0.75});
                if ($("#xyzFrameHolder").is(':empty')) {
                    let vars = this.populateVars(triggerElement);
                    window.MYWidget({
                        origin: window.location.origin,
                        vehicle: vars.vehicle,
                        vehicleType: vars.vehicleType,
                        onClose: function(el) {
                            $(el).modal('hide');
                            $("#xyzFrameHolder").empty();
                        },
                        onSubmit: function() {
                            $('#xyz-overlay').show();
                            $("#xyz-spinner").show();
                        }
                    }).render(('#xyzFrameHolder'));
                }
            });

            modal.on('hidden.bs.modal',function() {
                $(".modal-backdrop.in").css({opacity: 1});
            });
        },

        populateVars: function(element) {
            const srpVehicles = document.querySelectorAll('.srpVehicle');
            if (srpVehicles.length > 0) {
                const srpVehicle = element.parents('.srpVehicle').first();
                return {
                    vehicle: srpVehicle.data('name'),
                    vehicleType: srpVehicle.data('vehicletype')
                }
            } else {
                return {
                    vehicle: vehicle,
                    vehicleType: vehicleType
                }
            }
        }
    };
})();

(function(){
    ABCD.init();
})();


farjam
  • 2,089
  • 8
  • 40
  • 77
  • Just use an arrow function: `.on('shown.bs.modal', '#xyzModal', (event) => {`…`}`; otherwise `this` will be something else. – Sebastian Simon Feb 15 '23 at 17:05
  • @JorgeGuerreiro This isn’t relevant here: the function named `populateVars` is both defined and called inside the arrow IIFE. Accessing it like this is completely reasonable. – Sebastian Simon Feb 15 '23 at 17:07
  • 1
    Please get in the habit of providing a [mre] and putting in some debugging effort. Your issue boils down to _“`({ a(){ $("body").on("click", function(){ console.log(this.b, this); }); }, b: "hello" }).a();` doesn’t log `"hello"` and `this` is not the expected object”_. – Sebastian Simon Feb 15 '23 at 17:11
  • @SebastianSimon I changed it to use an arrow function, but looks like that piece is now running on the page load, and not when the modal window is opened, which is too early for me, because I need to populate those vars based on what page context the button is clicked on. – farjam Feb 15 '23 at 17:12
  • @farjam _“looks like that piece is now running on the page load”_ — If you’ve made the correct substitution, then this is impossible, unless you have _another_ mistake somewhere else or `shown.bs.modal` is indeed triggered on page load. Replacing `function (event)` by `(event) =>` is definitely the correct way forward in order to get the `this` value correct for `this.populateVars`. Verify that `this.populateVars` is being called correctly when the modal shows. Then you can start worrying about the page load issue — it seems unrelated to your original issue. – Sebastian Simon Feb 15 '23 at 17:20

0 Answers0