好的,所以背景故事是我们在页面上有一堆相同的(函数内)表单来为产品添加变体, 表格由三个主要部分组成。
该组件允许用户向产品添加属性。
每个属性都有一个visibility status
,一个attribute key
,一个attribute value
和一个delete button
,它们共同构成一行。
该组件还有一个Add Attribute
按钮,单击该按钮会在列表底部添加一个新行。
每个attribute key
选择列表都有一个new attribute
选项,在选择时会启动一个带有表单的模态对话框来输入新的属性名称,然后这个表单通过AJAX提交并返回一个ID,新的然后将选项附加到页面上的每个attribute key
选项,以允许选择它。
当在组件的实例中选择了一个键时,组中的所有其他attribute key
选择都会禁用该选项以防止重复的属性。
属性编辑器作为下面主表单的一部分提交。
<小时/>
该组件由变体的一般描述字段组成。
表单通过AJAX提交,并附加jQuery Validation Engine表单验证。
因为我们使用属性编辑器动态添加新输入,所以我们必须不断分离并重新附加验证引擎。
此组件处理在表单上显示/隐藏错误/成功/状态消息。
<小时/>
现在还有一些非常相似的形式,但是在几个事件处理程序上有轻微的变化,所以我想创建代码,这样我就可以随意替换它的一些部分,而不必复制整个代码。
因此,在按照this question提示后,我最终得到了以下代码,但我收到错误:Uncaught TypeError: object is not a function
就在这一行:var variantAlert = new VariantAlert(form);
我相信是因为我没有回复任何东西,但我不知道应该返回什么来获取代码来做我想做的事情!
短版
$(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);
}
})();
完整版
$(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's 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);
});
}
})();
答案 0 :(得分:1)
您的variantAlert使用类似
variantAlert.message('failed');
这意味着构造函数必须返回包含消息函数
的对象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 :(得分:0)
最后使用不同的方法让它工作,我的代码在下面供任何人比较和参考。
$(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's 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 = {};