I am using JsFormValidatorBundle bundle and liked it very much. Validation works when submit is clicked but I want validation to work immediately after field change without submitting the form
I want to run validation on custom event as below:
$('form')
.find('input, select, textarea')
.change(function(){
$(this).jsFormValidator('validate');
})
.jsFormValidator({
showErrors: function(errors, sourceId) {
var list = $(this).next('span.help-block');
list.find('.' + sourceId).remove();
if(errors.length){
$(this).closest('div.mgnBtm20').addClass('badData');
$(this).closest('div.mgnBtm20').removeClass('goodData');
}else{
$(this).closest('div.mgnBtm20').removeClass('badData');
$(this).closest('div.mgnBtm20').addClass('goodData');
}
for (var i in errors) {
var li = $('<span></span>', {
'class': sourceId,
'text': errors[i]
});
list.append(li);
}
}
});
and I get this error: Uncaught TypeError: Cannot read property 'transformers' of undefined fp_js_validator.js:536
I tried to use exactly same code as in https://github.com/formapro/JsFormValidatorBundle/blob/master/Resources/doc/3_12.md but unfortunately get the same error
Related
I found this question about how to return a custom validation error on form submit. This works for individual field errors, but how do I return a generic form error not pertaining to a particular field? I want to return a custom form submission error that appears above the submit button. The error I want to return doesn't have anything to do with a specific field, so I want to send back a generic error message that just shows at the bottom of the form.
There's no documentation on this that I can find.
First set your custom error to $form_data['errors'] in your submit hook.
$form_data['errors'] = ["My Custom Error Message"];
Now you need to display this error using front-end code.
Hook into "submit:response" on the front-end.
var mySubmitController = Marionette.Object.extend({
initialize: function () {
this.listenTo(
Backbone.Radio.channel("forms"),
"submit:response",
this.actionSubmit
);
},
actionSubmit: function (response) {
const errorNotice = document.querySelector(".nf-before-form nf-section");
errorNotice.innerHTML = "";
if (!response.errors || !response.errors.length) {
return;
}
const errors = response.errors;
errorNotice.insertAdjacentHTML(
"afterbegin",
"<strong>Your form submission encountered the following errors:</strong>"
);
errorNotice.insertAdjacentHTML("beforeend", "<ul>");
errors.forEach((error) => {
errorNotice.insertAdjacentHTML("beforeend", `<li>${error}</li>`);
});
errorNotice.insertAdjacentHTML("beforeend", "</ul>");
errorNotice.scrollIntoView({
behavior: "smooth"
});
}
});
jQuery(document).ready(function ($) {
new mySubmitController();
});
I've been trying to execute FB.XFBML.parse(); to reload all Facebook social plugins. I can successfully execute it in Template.sample.events({}) but not in Template.sample.onRendered() or Template.sample.rendered as I am getting an error saying that FB is undefined. My code is as follows:
Template.sample.onRendered(function() {
FB.XFBML.parse();
});
OR
Template.sample.rendered = function() {
FB.XFBML.parse();
}
How can I execute FB.XFBML.parse(); every time a template is loaded?
Just wrapped the method with a try-catch block. Noticed that for rendered is being called multiple times. The first pass fails saying FB is undefined while the succeeding passes succeeds.
Template.sample.rendered = function() {
try {
FB.XFBML.parse();
} catch (e) {
// Will normally crash but succeeding execution will be successful
}
}
I'm currently rendering bootstrap modals on my webpage using MeteorJS's "renderWithData" method to load each template when it's needed.
I'm running into an issue where my helper methods which access the data in the modal using "Blaze.getData()" will occasionally return undefined and I'm unsure how to fix that.
The only way I've been able to replicate the issue is by constantly creating/destroying the modals and there doesn't seem to be anything that specifically causes the issue.
Here are the steps I've been taking:
1) I instantiate the modal with the proper data
Template.Courses.events({
'click .share-course': function (e,t) {
var courseID = $(e.target).data('courseid');
Template.instance().activeCourse.set(
createModalWithData(
{
currentInstance: Template.instance().activeCourse.get(),
template: Template.Enrollment_Generator,
dataToRender: {courseID: courseID}
}
));
$('#generateEnrollmentURL').modal('show');
}
});
Also, here is the code for "createModalWithData":
// Create a modal with a specific data context
// If modal template already exists, destroy
// and re-create with the new data context.
// If a location to render isn't specified, renders
// content in the body .
// Parameters: [Object] data { currentInstance : Template || null,
// template : Template,
// dataToRender : Object,
// (optional) location : Element
// Return: Blaze Template Instance
createModalWithData = function createModalWithData(data) {
// Ensure data exists
if (_.isUndefined(data) || _.isNull(data)) {
throw "data cannot be null or undefined";
}
// If modal already exists, destroy it
if (!_.isNull(data.currentInstance)) {
Blaze.remove(data.currentInstance);
}
// If location is undefined, set to page body
if (_.isUndefined(data.location)) {
data.location = document.body;
}
// Render modal with dataToRender
return Blaze.renderWithData(data.template,
data.dataToRender,
data.location
);
};
2) I attempt to retrieve the data using "Blaze.getData()" within my modal template
Template.Enrollment_Generator.onCreated(function() {
var courseID = Blaze.getData().courseID; // Occasionally undefined
Meteor.subscribe('enrollment-codes',courseID);
});
So far I've attempted to replace the "onCreated" method with "onRendered" but still had the same issue.
It turns out the issue was within the click event. I had a nested span element within my share-course button:
<small class="share-course" data-courseid="{{_id}}">
Share
<span class="glyphicon glyphicon-share"></span>
</small>
This was messing up the way I was targeting my embedded courseID
Instead of Blaze.getData(), I should have also been using Template.currentData() to retrieve the data within my template
As stated here: https://forums.meteor.com/t/blaze-getdata-question/5688
I have the following template:
<template name="modalTest">
{{session "modalTestNumber"}} <button id="modalTestIncrement">Increment</button>
</template>
That session helper simply is a go-between with the Session object. I have that modalTestNumber initialized to 0.
I want this template to be rendered, with all of it's reactivity, into a bootbox modal dialog. I have the following event handler declared for this template:
Template.modalTest.events({
'click #modalTestIncrement': function(e, t) {
console.log('click');
Session.set('modalTestNumber', Session.get('modalTestNumber') + 1);
}
});
Here are all of the things I have tried, and what they result in:
bootbox.dialog({
message: Template.modalTest()
});
This renders the template, which appears more or less like 0 Increment (in a button). However, when I change the Session variable from the console, it doesn't change, and the event handler isn't called when I click the button (the console.log doesn't even happen).
message: Meteor.render(Template.modalTest())
message: Meteor.render(function() { return Template.modalTest(); })
These both do exactly the same thing as the Template call by itself.
message: new Handlebars.SafeString(Template.modalTest())
This just renders the modal body as empty. The modal still pops up though.
message: Meteor.render(new Handlebars.SafeString(Template.modalTest()))
Exactly the same as the Template and pure Meteor.render calls; the template is there, but it has no reactivity or event response.
Is it maybe that I'm using this less packaging of bootstrap rather than a standard package?
How can I get this to render in appropriately reactive Meteor style?
Hacking into Bootbox?
I just tried hacked into the bootbox.js file itself to see if I could take over. I changed things so that at the bootbox.dialog({}) layer I would simply pass the name of the Template I wanted rendered:
// in bootbox.js::exports.dialog
console.log(options.message); // I'm passing the template name now, so this yields 'modalTest'
body.find(".bootbox-body").html(Meteor.render(Template[options.message]));
body.find(".bootbox-body").html(Meteor.render(function() { return Template[options.message](); }));
These two different versions (don't worry they're two different attempts, not at the same time) these both render the template non-reactively, just like they did before.
Will hacking into bootbox make any difference?
Thanks in advance!
I am giving an answer working with the current 0.9.3.1 version of Meteor.
If you want to render a template and keep reactivity, you have to :
Render template in a parent node
Have the parent already in the DOM
So this very short function is the answer to do that :
renderTmp = function (template, data) {
var node = document.createElement("div");
document.body.appendChild(node);
UI.renderWithData(template, data, node);
return node;
};
In your case, you would do :
bootbox.dialog({
message: renderTmp(Template.modalTest)
});
Answer for Meteor 1.0+:
Use Blaze.render or Blaze.renderWithData to render the template into the bootbox dialog after the bootbox dialog has been created.
function openMyDialog(fs){ // this can be tied to an event handler in another template
<! do some stuff here, like setting the data context !>
bootbox.dialog({
title: 'This will populate with content from the "myDialog" template',
message: "<div id='dialogNode'></div>",
buttons: {
do: {
label: "ok",
className: "btn btn-primary",
callback: function() {
<! take some actions !>
}
}
}
});
Blaze.render(Template.myDialog,$("#dialogNode")[0]);
};
This assumes you have a template defined:
<template name="myDialog">
Content for my dialog box
</template>
Template.myDialog is created for every template you're using.
$("#dialogNode")[0] selects the DOM node you setup in
message: "<div id='dialogNode'></div>"
Alternatively you can leave message blank and use $(".bootbox-body") to select the parent node.
As you can imagine, this also allows you to change the message section of a bootbox dialog dynamically.
Using the latest version of Meteor, here is a simple way to render a doc into a bootbox
let box = bootbox.dialog({title:'',message:''});
box.find('.bootbox-body').remove();
Blaze.renderWithData(template,MyCollection.findOne({_id}),box.find(".modal-body")[0]);
If you want the dialog to be reactive use
let box = bootbox.dialog({title:'',message:''});
box.find('.bootbox-body').remove();
Blaze.renderWithData(template,function() {return MyCollection.findOne({_id})},box.find(".modal-body")[0]);
In order to render Meteor templates programmatically while retaining their reactivity you'll want to use Meteor.render(). They address this issue in their docs under templates.
So for your handlers, etc. to work you'd use:
bootbox.dialog({
message: Meteor.render(function() { return Template.modalTest(); })
});
This was a major gotcha for me too!
I see that you were really close with the Meteor.render()'s. Let me know if it still doesn't work.
This works for Meteor 1.1.0.2
Assuming we have a template called changePassword that has two fields named oldPassword and newPassword, here's some code to pop up a dialog box using the template and then get the results.
bootbox.dialog({
title: 'Change Password',
message: '<span/>', // Message can't be empty, but we're going to replace the contents
buttons: {
success: {
label: 'Change',
className: 'btn-primary',
callback: function(event) {
var oldPassword = this.find('input[name=oldPassword]').val();
var newPassword = this.find('input[name=newPassword]').val();
console.log("Change password from " + oldPassword + " to " + newPassword);
return false; // Close the dialog
}
},
'Cancel': {
className: 'btn-default'
}
}
});
// .bootbox-body is the parent of the span, so we can replace the contents
// with our template
// Using UI.renderWithData means we can pass data in to the template too.
UI.insert(UI.renderWithData(Template.changePassword, {
name: "Harry"
}), $('.bootbox-body')[0]);
I am trying to select events on fullcalendar, based on user selection.
Example: if user selects class A, then all classes with the same ID should turn green (using applied className).
I am having trouble applying classes to the other events that I can successfully select by ID. I guess my issue is combining the event objects with jQuery objects.
sample code:
eventClick: function(event) {
$(this).addClass("reg_selected"); //this works fine on selected event
var selectedID = event.id
alert(selectedID); //get event.ID, and use it to find similar ones.
var similarEvents = $("#calendar").fullCalendar('clientEvents',selectedID).addClass("reg_selected");
the error I get is:
addClass is not a function
I also tried this method of looping, and got the same error:
for (var i = 0; similarEvents.length > i ; i++){
alert(similarEvents[i].title);
similarEvents[i].className("reg_selected");
}
the alert() worked, but the className() generated the same error as above
This answer for a very similar situation, but when event classes are selected with round-trip to the event source for possible persistence in the db or checks.
Class name can be specified in the event object in the source as follows (start and end given for the context only):
[{
...
"className": "selected-event",
"start": '2017-05-01T08:30:00.0',
"ends": '2017-05-01T09:00:00.0',
...
}, ...]
The idea is that user clicks the event; ajax call to select events goes to backend; onsuccess, frontend javascript does$calendar.fullCalendar('rerenderEvents'); and receives the event source with events' classes. The immediate child of .fc-event-container gets the specified class, in the example above - selected-event.
As a result, the selection can be persisted on the backend.
clientEvents returns an array of matching objects. You need to iterate through the array (in your case similarEvents) and call addClass for each item
Update:
There is also issues using an id to update multiple events, using a filter function instead is a better way to go.
eventClick: function(event) {
var similarEvents = $("#calendar").fullCalendar('clientEvents', function(e) { return e.test === event.test });
for (var i = 0; similarEvents.length > i ; i++){
similarEvents[i].className = 'reg_selected';
$('#calendar').fullCalendar('updateEvent', similarEvents[i]);
}
},
See jsfiddle
For fullcalendar add event class, id and title see this.
if($('#eventTitle').val() == "Avilable") {
eventClass = "avilable";
}else {
eventClass = "unavilable";
}
$myCalendar.fullCalendar('renderEvent', {
id:response,
title: title.val(),
start: start.val(),
end: end.val(),
allDay: true,
className: eventClass,
color: color
}, true
);
I was able to get it working with the following code:
eventRender: function (eventObj, $el) {
$el.addClass(eventObj.ClassName);
},
eventObj.ClassName = "calendar-priority-warning"