Javascript OOP not working: Uncaught TypeError: object is not a function

Well, so the background - we have a bunch of identical (in function) forms on the page to add options to the product. The form consists of three main parts.

Attribute Editor

enter image description here
This component allows the user to add attributes to the product. Each attribute has visibility status , attribute key , a attribute value and a delete button , which together form one line.

The component also has an Add Attribute button that, when clicked, adds a new line to the bottom of the list.

Each attribute key selection list has a new attribute parameter, which, after selection, launches a modal dialog box with a form for entering a new attribute name, this form is then sent via AJAX and returns an identifier, then a new parameter is added for each attribute key select on the page so that it can to be selected.

When a key is selected in an instance of a component, all other attribute key are selected in the group so that this parameter is disabled to prevent duplication of attributes.

The attribute editor is submitted as part of the main form below.


Basic form

enter image description here
This component consists of general variant description fields. The form is submitted via AJAX and has a jQuery validation mechanism attached to validate the form.

Since we add the new input dynamically using the attribute editor, we must constantly disconnect and reattach the verification mechanism.


Alert system

enter image description here
This component handles showing / hiding error / success / status messages in the form on the form.


Problem

Now there are also several forms that are very similar, but are small options for a couple of event handlers, so I wanted to create the code so that I could replace its pieces as I wanted without having to copy all the code.

So, after the following tips from this question , I ended up with the code below, but I get the error: Uncaught TypeError: object is not a function , which is in this line: var variantAlert = new VariantAlert(form); which, I believe, is because I am not returning anything, but I do not know what I have to return in order to get the code to do what I want!

Short version

 $(function () { $("form.variant-form").each(function (i, form) { var variantAlert = new VariantAlert(form); var variantForm = new VariantForm(form, variantAlert); variantForm.init(); var attributeEditor = new AttributeEditor(form, variantForm); attributeEditor.init(); }); }); var AttributeEditor = (function (form, formSetup) { form = $('form'); var someVar = 123; var init = function () { someEventHandler(); }; var someEventHandler = function () { $('.selector', form).on('some event', function (e) { form.css('background-color', '#f00'); }); }; return AttributeEditor; })(); var VariantForm = (function (form, variantAlert) { form = $('form'); var init = function () { anotherEventHandler(); }; var anotherEventHandler = function () { $('.anotherSelector', form).on('another event', function () { form.doStuff(); }); }; })(); var VariantAlert = (function (form) { var timer; form = $('form'); var message = function (type, message) { doMoreStuff(type, message); } })(); 

Full version

 $(function () { /********************************* * Loop over each variant and setup * the attribute editor and form *********************************/ $("form.variant-form").each(function (i, form) { var variantAlert = new VariantAlert(form); var variantForm = new VariantForm(form, variantAlert); variantForm.init(); var attributeEditor = new AttributeEditor(form, variantForm); attributeEditor.init(); }); }); var AttributeEditor = (function (form, formSetup) { /********************************* * Variables *********************************/ form = $('form'); var template = $('.variant_demo_row', form); var attributes = $('.variant_select', form).length; var modal = form.siblings('.newAttribute').appendTo('body'); var manualHide = false; var triggerSelect = null; var oldOption = null; var init = function () { //setup the handlers //doing it this way allows us to overwrite the individual handlers with ease addNewAttributeHandler(); removeAttributeHandler(); selectFocusHandler(); selectChangeHandler(); attributeVisibilityHandler(); modalFormSubmissionHandler(); modalShowHandler(); modalCancelClickHandler(); }; /********************************* * Add new attribute button handler *********************************/ var addNewAttributeHandler = function () { $('.variant_attribute_add_new a', form).on('click keypress', function (e) { form.css('background-color', '#f00'); //patched support for enter key if (e.type === 'keypress' && e.which != 13) { return true; } //clone the template row so we can edit it var newRow = template.clone().css('display', 'none').removeClass('hidden variant_demo_row').addClass('variant_row'); //give each element in the clone it unique name $('.variant_select', newRow).prop('name', 'attribute_key_' + attributes); $('.variant_input', newRow).prop('name', 'attribute_value_' + attributes); $('.variant_visible', newRow).prop('name', 'attribute_visible_' + attributes); //insert the new attribute row at the bottom of the attributes newRow.insertBefore($('.variant_attribute_add_new', form)).show('fast', function () { $('select', newRow).focus(); }); //we have added new nodes so we need to reset the validationEngine form.validationEngine('detach'); formSetup.init(); attributes++; }); }; /********************************* * Remove attribute button handler *********************************/ var removeAttributeHandler = function () { form.on('click keypress', '.removeAttribute', {}, function (e) { //patched support for enter key if (e.type === 'keypress' && e.which != 13) { return true; } attributes--; var val = $(this).siblings('select').val(); //re-enable whatever attribute key was in use if (val != "") { $('.variant_select option[value=' + val + ']', form).removeAttr('disabled'); } //animate the removal of the attribute $(this).closest('.controls-row').hide('fast', function () { $(this).remove(); }); }); }; /********************************* * Attribute key select focus handler *********************************/ var selectFocusHandler = function () { form.on('focus', '.variant_select', {}, function () { //store the old option so we know what option to //re-enable if a change is made oldOption = $('option:selected', this).val(); }); }; /********************************* * Attribute key select change handler *********************************/ var selectChangeHandler = function () { form.on('change', '.variant_select', {}, function () { var select = $(this); //empty class is used for "placeholder" simulation select.removeClass('empty'); //re-enable whatever option was previously selected if (oldOption !== null) { $('.variant_select option[value=' + oldOption + ']', form).removeAttr('disabled'); } if ($('option:selected', select).hasClass('newAttribute')) { //Add new attribute selected triggerSelect = select; modal.modal('show'); } else if ($('option:selected', select).val() == "") { //Placeholder selected select.addClass('empty'); } else { //Value selected //disable the selected value in other attribute key selects $('.variant_select', form).not(select).children('option[value=' + select.val() + ']').prop('disabled', 'disabled'); } oldOption = select.val(); }); }; /********************************* * Toggle visibility button handler *********************************/ var attributeVisibilityHandler = function () { form.on('click', '.toggleVisibility', {}, function () { //the titles of the button var hidden = 'Hidden Attribute'; var visible = 'Visible Attribute'; var btn = $(this); var icon = btn.children('i'); var box = btn.siblings('.variant_visible'); //toggle the state between visible and hidden btn.toggleClass('btn-success btn-warning').attr('title', btn.attr('title') == hidden ? visible : hidden); icon.toggleClass('icon-eye-open icon-eye-close'); box.prop("checked", !box.prop("checked")) }); }; /********************************* * New attribute submission handler *********************************/ var modalFormSubmissionHandler = function () { $('.newAttributeForm', modal).validationEngine('attach', { onValidationComplete:function (form, status) { if (status) { var text = $('.newAttributeName', modal).val(); $('.newAttributeName', modal).val(''); form.spin(); $.ajax({ type:'POST', url:'/cfox/cart/variants/addattribute', data:{name:text}, success:function (data) { //add new attribute key to attribute key selects everywhere $('.variant_select').append($('<option>', { value:data.id}).text(data.name)); //set the triggering selects value to the new key triggerSelect.val(data.id); triggerSelect.trigger('change'); manualHide = true; modal.modal('hide'); triggerSelect.siblings('input').focus(); form.spin(false); }, dataType:'JSON' }); } }}); }; var modalCancelClickHandler = function () { $('.btn-danger', modal).on('click', function () { if (!manualHide) { triggerSelect[0].selectedIndex = 1; triggerSelect.trigger('change'); } manualHide = false; }); }; var modalShowHandler = function () { modal.on('show shown', function () { $('.newAttributeName', modal).focus(); }); } return AttributeEditor; })(); var VariantForm = (function (form, variantAlert) { /********************************* * Variables *********************************/ form = $('form'); var init = function () { nameChangeHandler(); submitHandler(); }; /********************************* * Variant name change handler * Changes the heading on the accordion if the * name form input changes *********************************/ var nameChangeHandler = function () { var accordion_heading = form.closest('.accordion-body').siblings('.accordion-heading').find('.accordion-toggle'); $('.name-input', form).on('change', function () { accordion_heading.text($(this).val()); }); }; /********************************* * Form submit handler *********************************/ var submitHandler = function () { form.validationEngine('attach', { onValidationComplete:function (form, status) { if (status == true) { $.ajax({ type:'POST', url:form.attr('action'), data:form.serialize(), dataType:'json', beforeSend:function () { cfox.disableForm(form); form.spin(); form.children('.variant_status_message').hide('fast'); }, success:function (response) { cfox.enableForm(form);//need to do this here so browser doesn't cache disabled fields if (typeof response != "object" || response === null) { variantAlert.message('failed'); } else { switch (response.status) { case 0: variantAlert.message('errors', response.errors); break; case 1: variantAlert.message('success'); break; default: variantAlert.message('failed'); break; } } form.spin(false); }, error:function () { variantAlert.message('failed'); form.spin(false); cfox.enableForm(form); } }); } } }); } })(); var VariantAlert = (function (form) { /********************************* * Variables *********************************/ var timer; form = $('form'); /********************************* * handles showing/hiding any messages * in the variant forms *********************************/ var message = function (type, message) { var alert; clearTimeout(timer); $('.variant_status_message', form).hide('fast'); if (type == 'success') { alert = $('.variant_status_message.success', form); } else if (type == 'errors') { alert = $('.variant_status_message.errors', form); $('.alert-message', alert).html(message); } else if (type == 'failed') { alert = $('.variant_status_message.failed', form); } alert.show('fast', function () { $('html, body').animate({ scrollTop:alert.closest('.accordion-group').offset().top }, 150, 'linear'); timer = setTimeout(function () { alert.hide('fast') }, 5000); }); } })(); 
+4
source share
2 answers

Your optionAlert uses as

  variantAlert.message('failed'); 

This means that the constructor must return an object containing the message function

 var VariantAlert = function (form) { var timer; /********************************* * handles showing/hiding any messages * in the variant forms *********************************/ var message = function (type, message) { var alert; clearTimeout(timer); $('.variant_status_message', form).hide('fast'); if (type == 'success') { alert = $('.variant_status_message.success', form); } else if (type == 'errors') { alert = $('.variant_status_message.errors', form); $('.alert-message', alert).html(message); } else if (type == 'failed') { alert = $('.variant_status_message.failed', form); } alert.show('fast', function () { $('html, body').animate({ scrollTop:alert.closest('.accordion-group').offset().top }, 150, 'linear'); timer = setTimeout(function () { alert.hide('fast') }, 5000); }); } return { message: message }; } 
+1
source

Finally, he started working using another method, my code is below for anyone who can compare and reference.

 $(function () { /********************************* * Loop over each variant and setup * the attribute editor and form *********************************/ var editors = []; $("form.variant-form").each(function (i, form) { var variantEditor = new VariantEditor(form); editors.push(variantEditor); variantEditor.attributeEditor.init(); variantEditor.form.init(); }); }); var VariantEditor = function (form) { var that = this; that.formElement = $(form); /********************************* * Sets up the attribute editor *********************************/ that.attributeEditor = { /********************************* * Variables *********************************/ template:null, attribute:null, modal:null, manualHide:false, triggerSelect:null, oldOption:null, /********************************* * Sets up the attribute editor *********************************/ init:function () { var that = this; //The Template row that.template = $('.variant_demo_row', that.formElement); //How many attributes are pre-loaded that.attributes = $('.variant_select', that.formElement).length; //Append the attribute editor modal to the body to avoid style corruption that.modal = that.formElement.siblings('.newAttribute').appendTo('body'); //setup the handlers //doing it this way allows us to overwrite the individual handlers with ease that.addNewAttributeHandler(); that.removeAttributeHandler(); that.selectFocusHandler(); that.selectChangeHandler(); that.attributeVisibilityHandler(); that.modalFormSubmissionHandler(); that.modalShowHandler(); that.modalCancelClickHandler(); $('.variant_select', that.formElement).each(function (i, select) { that.oldOption = null; $(select).change(); }); }, /********************************* * Add new attribute button handler *********************************/ addNewAttributeHandler:function () { var that = this; $('.variant_attribute_add_new a', that.formElement).on('click keypress', function (e) { //patched support for enter key if (e.type === 'keypress' && e.which != 13) { return true; } //clone the template row so we can edit it var newRow = that.template.clone().css('display', 'none').removeClass('hidden variant_demo_row').addClass('variant_row'); //give each element in the clone it unique name $('.variant_select', newRow).prop('name', 'attribute_key_' + that.attributes); $('.variant_input', newRow).prop('name', 'attribute_value_' + that.attributes); $('.variant_visible', newRow).prop('name', 'attribute_visible_' + that.attributes); //insert the new attribute row at the bottom of the attributes newRow.insertBefore($('.variant_attribute_add_new', that.formElement)).show('fast', function () { $('select', newRow).focus(); }); //we have added new nodes so we need to reset the validationEngine that.formElement.validationEngine('detach'); that.form.init(); that.attributes++; }); }, /********************************* * Remove attribute button handler *********************************/ removeAttributeHandler:function () { var that = this; that.formElement.on('click keypress', '.removeAttribute', {}, function (e) { //patched support for enter key if (e.type === 'keypress' && e.which != 13) { return true; } that.attributes--; var val = $(this).siblings('select').val(); //re-enable whatever attribute key was in use if (val != "") { $('.variant_select option[value=' + val + ']', that.formElement).removeAttr('disabled'); } //animate the removal of the attribute $(this).closest('.controls-row').hide('fast', function () { $(this).remove(); }); }); }, /********************************* * Attribute key select focus handler *********************************/ selectFocusHandler:function () { var that = this; that.formElement.on('focus', '.variant_select', {}, function () { //store the old option so we know what option to //re-enable if a change is made that.oldOption = $('option:selected', this).val(); }); }, /********************************* * Attribute key select change handler *********************************/ selectChangeHandler:function () { var that = this; that.formElement.on('change', '.variant_select', {}, function () { var select = $(this); //empty class is used for "placeholder" simulation select.removeClass('empty'); //re-enable whatever option was previously selected if (that.oldOption !== null) { $('.variant_select option[value=' + that.oldOption + ']', that.formElement).removeAttr('disabled'); } if ($('option:selected', select).hasClass('newAttribute')) { //Add new attribute selected that.triggerSelect = select; that.modal.modal('show'); } else if ($('option:selected', select).val() == "") { //Placeholder selected select.addClass('empty'); } else { //Value selected //disable the selected value in other attribute key selects $('.variant_select', that.formElement).not(select).children('option[value=' + select.val() + ']').prop('disabled', 'disabled'); } that.oldOption = select.val(); }); }, /********************************* * Toggle visibility button handler *********************************/ attributeVisibilityHandler:function () { var that = this; that.formElement.on('click', '.toggleVisibility', {}, function () { //the titles of the button var hidden = 'Hidden Attribute'; var visible = 'Visible Attribute'; var btn = $(this); var icon = btn.children('i'); var box = btn.siblings('.variant_visible'); //toggle the state between visible and hidden btn.toggleClass('btn-success btn-warning').attr('title', btn.attr('title') == hidden ? visible : hidden); icon.toggleClass('icon-eye-open icon-eye-close'); box.prop("checked", !box.prop("checked")) }); }, /********************************* * New attribute submission handler *********************************/ modalFormSubmissionHandler:function () { var that = this; $('.newAttributeForm', that.modal).validationEngine('attach', { onValidationComplete:function (form, status) { if (status) { var text = $('.newAttributeName', that.modal).val(); $('.newAttributeName', that.modal).val(''); form.spin(); $.ajax({ type:'POST', url:'/cfox/cart/variants/addattribute', data:{name:text}, success:function (data) { //add new attribute key to attribute key selects everywhere $('.variant_select').append($('<option>', { value:data.id}).text(data.name)); //set the triggering selects value to the new key that.triggerSelect.val(data.id); that.triggerSelect.trigger('change'); that.manualHide = true; that.modal.modal('hide'); that.triggerSelect.siblings('input').focus(); form.spin(false); }, dataType:'JSON' }); } }}); }, modalCancelClickHandler:function () { var that = this; $('.btn-danger', that.modal).on('click', function () { if (!that.manualHide) { that.triggerSelect[0].selectedIndex = 1; that.triggerSelect.trigger('change'); } that.manualHide = false; }); }, modalShowHandler:function () { var that = this; that.modal.on('show shown', function () { $('.newAttributeName', that.modal).focus(); }); } }; /********************************* * Sets up the variant main form * The above function focuses on setting up the attribute editor * This function sets up the rest of the form and handles the * form submissions *********************************/ that.form = { init:function () { var that = this; that.nameChangeHandler(); that.submitHandler(); that.statusChangeHandler(); }, /********************************* * Variant name change handler * Changes the heading on the accordion if the * name form input changes *********************************/ nameChangeHandler:function () { var that = this; var accordion_heading = that.formElement.closest('.accordion-body').siblings('.accordion-heading').find('.accordion-toggle .name'); $('.name-input', that.formElement).on('change', function () { accordion_heading.text($(this).val()); }); }, statusChangeHandler: function(){ var that = this; console.log($('input[name=status]', that.formElement).parent()); $('input[name=status]', that.formElement).parent().on('click keypress', function(e){ //patched support for enter key if (e.type === 'keypress' && e.which != 13) { return true; } console.log('called'); var checked = $(this).prop('checked'); var label = that.formElement.closest('.accordion-body').siblings('.accordion-heading').find('.accordion-toggle .label'); label.text(checked ? 'Online' : 'Offline').toggleClass('label-important label-success'); }); }, /********************************* * Form submit handler *********************************/ submitHandler:function () { var that = this; that.formElement.validationEngine('attach', { onValidationComplete:function (form, status) { if (status == true) { $.ajax({ type:'POST', url:form.attr('action'), data:form.serialize(), dataType:'json', beforeSend:function () { cfox.disableForm(form); form.spin(); form.children('.variant_status_message').hide('fast'); }, success:function (response) { cfox.enableForm(form);//need to do this here so browser doesn't cache disabled fields if (typeof response != "object" || response === null) { that.message('failed'); } else { switch (response.status) { case 0: that.message('errors', response.errors); break; case 1: that.message('success'); break; default: that.message('failed'); break; } } form.spin(false); }, error:function () { cfox.alert('alert-error', "An error was encountered when submitting this form. Please try again."); form.spin(false); cfox.enableForm(form); } }); } } }); } }; /********************************* * handles showing/hiding any messages * in the variant forms *********************************/ that.message = function (type, message) { var that = this; var alert; clearTimeout(that.timer); $('.variant_status_message', that.formElement).hide('fast'); if (type == 'success') { alert = $('.variant_status_message.success', that.formElement); } else if (type == 'errors') { alert = $('.variant_status_message.errors', that.formElement); $('.alert-message', alert).html(message); } else if (type == 'failed') { alert = $('.variant_status_message.failed', that.formElement); } alert.show('fast', function () { $('html, body').animate({ scrollTop:alert.closest('.accordion-group').offset().top }, 150, 'linear'); that.timer = setTimeout(function () { alert.hide('fast') }, 5000); }); } that.attributeEditor.formElement = that.formElement; that.attributeEditor.form = that.form; that.attributeEditor.message = that.message; that.form.formElement = that.formElement; that.form.attributeEditor = that.attributeEditor; that.form.message = that.message; that.message.formElement = that.formElement; that.message.attributeEditor = that.attributeEditor; that.message.form = that.form; return that; }; VariantEditor.prototype = {}; 
0
source

Source: https://habr.com/ru/post/1444027/


All Articles