KnockoutJS Validation template and multi models - javascript

I have two pages, the first is a login page, simple :
Model KnockoutJs
function Login(){
var self=this;
self.email = ko.observable().extend({ email: true, required: true });
self.password = ko.observable().extend({ required: true});
}
Model Binding
$(function () {
ko.validation.configure({
insertMessages: true,
decorateElement: true,
errorElementClass: 'validation',
messageTemplate: "ValidationTemplate",
errorsAsTitle: false
});
var login = new Login();
ko.applyBindings(login);
});
Template Definition
<script type="text/html" id="ValidationTemplate">
<span data-bind="attr: { error: field.error },
visible: field.isModified() && !field.isValid(),
event: { mouseover: layout.errorTooltip }"
class="glyphicon glyphicon-exclamation-sign f-validation-message"></span>
</script>
Everything works fine, the little icon appears over the input which gets red borders.
Then the other page, with models hierarchy :
Model KnockoutJs Parent
function Parent()
{
var self=this;
self.child= new Child();
}
Model KnockoutJs Child
function Child()
{
var self=this;
self.val1= ko.observable().extend({ required: true });
self.val2= ko.observable().extend({ required: true });
}
Model Parent Binding
$(function () {
ko.validation.configure({
insertMessages: true,
decorateElement: true,
errorElementClass: 'validation',
messageTemplate: "ValidationTemplate",
errorsAsTitle: false
});
var parent= new Parent();
ko.applyBindings(parent);
});
The inputs are in this case included in a with block
<div data-bind="with:$root.child">
...
</div>
The validation template is the same.
So, the icon does not appear but the borders, yes.
When I check the code, KnouckoutJs did not "spread" the template on each input.
The only difference is the multi models system, but not sure how it impacts the binding?
Thank you for your help.
Yoann

Ok, I found the problem, it was not linked at all with the multi model or validation template.
I was binding data like follow :
//data: JS object
self.obsProp(ko.mapping.fromJS(data));
self.obsProp().value1.extend({required:true});
self.obsProp().value2.extend({required:true});
WRONG Way, the correct way to map the data with validation :
var validationMapping = {
value1: {
create: function(options) {
return ko.observable(options.data).extend( {required: true} );
}
},
value2: {
create: function(options) {
return ko.observable(options.data).extend( {required: true} );
}
}
};
self.obsProp(ko.mapping.fromJS(data,validationMapping));
And everything works fine.
Thanks :)

Related

access angular 2 variables in jquery

Following the example of Asad in this related question, I do not know how to refer to the object this of the javascript itself and angular varibale.
// Original code, the angular variable don't work
$.FroalaEditor.RegisterCommand('insert', {
title: 'Insert Read More',
focus: true,
undo: true,
refreshAfterCallback: true,
callback: function () {
this.html.insert('<span class="readMore">'+this.angularVariable+' </span>');
}
});
// using fat arrows, the angular variable works but this.html.insert fails
$.FroalaEditor.RegisterCommand('insert', {
title: 'Insert Read More',
focus: true,
undo: true,
refreshAfterCallback: true,
callback: () => {
this.html.insert('<span class="readMore">'+this.angularVariable+' </span>');
}
});

Assign dynamically loaded html element to variable

I'm trying to validate fields loaded using jQuery $().load();.
with jquery validation plugin (jqueryvalidation.org)
My code is:
var contactForm = $('#contact-form');
contactForm.validate({
rules: {
name: {
required: true,
},
email: {
required: true,
email: true
},
phone: {
required: false,
phoneUS: true
},
message: {
required: true
}
}
});
Unfortunately it doesn't work with elements loaded with load();
After some google searching I found some solution but they all are aimed on "events", not to pass dynamically added input to variable:
$(document).on('click','#submit',function() {});
I also tried to approach it with this code bellow :
$('.home').click(function() {
$("#article").load("./ #article > *",function(){
contactForm = $(this).find("#contact-form");
});
Also doesn't work though :(
I will appreciate any clues and help.
Try to add them dynamically :
$("#contact-form").find('input').each(function () {
$(this).rules("add", {
required: true
});
});
Hope this helps.

Multiple instances of jQuery Validation Plugin on a JSP page

SOLVED: $.extend() twice in a row simply overwrites the previous object because objects are passed by reference. My fix was to do this:
var updateUserObj = $.extend({}, validationPluginDefaults);
var newUserObj = $.extend({}, validationPluginDefaults);
Then the validators are executed like this:
var whateverValidator = $.extend(updateUserObj,{new rules})
Update: Further research shows that what I'm trying to do appears to be correct but the problem may be in the object I am extending - "validationPluginDefaults"). Here it is below:
var validationPluginDefaults = {
ignore: [],
errorElement: 'p', //default input error message container <p>
errorClass: 'text-error', // default input error message class
focusInvalid: true, //focus on the first invalid field
messages: {},
invalidHandler: function (event, validator) { //display error alert on form submit
},
highlight: function (el) { // hightlight error inputs
//jQuery(el).closest('.control-group').addClass('error'); // set error class to the control group
},
success: function (err, el) {
jQuery(el).next(".text-error").hide();
jQuery(el).next(".text-error").remove();
},
errorPlacement: function (error, element) {
error.insertAfter(element);
},
onfocusin: function () {},
onfocusout: function () {}
};
From my testing it appears that even though I have two separate forms on a JSP page and each has different validation that I can still only use one jQuery Validate Plugin instance per page; is this correct?
Here's my code:
// Define validation rules for form one
var formOneValidator = $.extend(validationPluginDefaults,{
rules: {
firstName: {
minlength: 2,
onlyAlphaAndHyphen: true,
required: true,
},
lastName: {
minlength: 2,
required: true,
},
email: {
required: true,
email: true
}
}
});
var formTwoValidator = $.extend(validationPluginDefaults,{
rules: {
newFirstName: {
minlength: 2,
onlyAlphaAndHyphen: true,
required: true,
},
newLastName: {
minlength: 2,
required: true,
},
newEmail: {
required: true,
email: true,
}
}
});
$("#formOne").validate(formOneValidator);
$("#formTwo").validate(formTwoValidator);
What happens here is that formOne validator takes on all the rules of formTwoValidator after this code executes.
Is the solution to have only one validator and swap out rules depending on which form I'm in? It just seems strange that I wouldn't be able to define separate validators for each form on the page. Am I fundamentally misunderstanding something here?
you could use knockout and give each page a different view model which handles the different validation. That is how i would do it but i'm sure there will be a better answer.

Knockout Validation on Nested View Model

I am looking to use a combination of the KnockoutJS libarary, the Knockout.Mapping plugin, and the Knockout-Validation plugin to display some data that the user can manipulate.
My data is coming over as a nested object from an AJAX call, and I run that data through the mapping plugin to create a Knockout view model, customizing the validation rules with the mapping options object in ko.mapping.fromJS.
I have been successful in getting objects at the first layer (name in the Fiddle below) to show a message if the field is empty, however objects that are nested (IntroData.PlanName) do not show the validation message. Do I need to setup the mapping object differently for these nested objects?
ViewModel (sample of what is coming in my AJAX call):
var stuff = {
IntroData: {
PlanName: 'Test'
},
name: 'tes2s3t'
};
Mapping:
var validationMapping = {
IntroData: {
PlanName: {
create: function (options) {
return ko.observable(options.data).extend({
required: true
});
}
}
},
name: {
create: function (options) {
return ko.observable(options.data).extend({
required: true
});
}
}
};
Hookup:
ko.validation.init({
registerExtenders: true,
messagesOnModified: true,
insertMessages: true,
parseInputAttributes: true,
messageTemplate: null,
grouping: {
deep: true
}
}, true);
window.viewModel = ko.validatedObservable(ko.mapping.fromJS(stuff, validationMapping));
ko.applyBindings(window.viewModel);
Fiddle: http://jsfiddle.net/odxv53g9/5/
Thanks!
The documentation is not clear on this, but apparently ko.mapping.fromJS() ignores nested mappings, so the "create" method for PlanName never gets called.
You could add an explicit mapping for IntroData instead:
IntroData: {
create: function (options) {
var nestedMapping = {
PlanName: {
create: function (options) {
return ko.observable(options.data).extend({
required: true
});
}
}
}
return ko.mapping.fromJS(options.data, nestedMapping);
}
}
Here is a working fiddle: http://jsfiddle.net/odxv53g9/6/

Backbone Collection Can't Remove Items

I've got a Backbone Model called Delivery. I then create a collection of Deliveries called DeliveryList backed by LocalStorage. In my Marionette.ItemView for displaying items in the collection, I have a method to remove items:
removeDeliveryOption: function() {
Deliveries.remove(this.model.get("id"));
}
For some reason, this removes the item from the Marionette.CompositeView when I click the remove button, but when I reload the page the same number of items always reappear.
It's worth noting that when I delete the item, it always reappears with the default optionName "Free Delivery". I'm using both defaults and a schema in the model because I'm using the Backbone-forms plugin (https://github.com/powmedia/backbone-forms).
Any help is greatly appreciated!
var Delivery = Backbone.Model.extend({
defaults: function () {
return {
order: Deliveries.nextOrder(),
optionName: "Free Delivery",
shipToState: "Hawaii",
zipCodes: "96813",
perOrderFee: "0.00",
perItemFee: "0.00"
};
},
schema: {
optionName: { type: 'Text', validators: ['required'] },
shipToState: { type: 'Select', options: getStateNames(), validators: ['required'] },
zipCodes: { type: 'Text', validators: ['required'] },
perOrderFee: { type: 'Text', validators: ['required'] },
perItemFee: { type: 'Text', validators: ['required'] },
}
});
var DeliveryList = Backbone.Collection.extend({
model: Delivery,
localStorage: new Backbone.LocalStorage("deliverylist-backbone"),
nextOrder: function () {
if (!this.length) return 1;
return this.last().get('order') + 1;
},
comparator: 'order'
});
var Deliveries = new DeliveryList;
var deliveryView = Marionette.ItemView.extend({
//tagName: "li",
template: "#delivery-item-template",
events: {
"click #removeThis": "removeDeliveryOption",
},
removeDeliveryOption: function() {
Deliveries.remove(this.model.get("id"));
}
});
var DeliveriesView = Marionette.CompositeView.extend({
initialize: function() {
Deliveries.fetch();
},
template: '#deliveries-view-template',
itemView: deliveryView,
events: {
"click #addShipping": "addDeliveryOption",
},
addDeliveryOption: function() {
var editDeliveryForm = new Backbone.Form({
template: _.template($("#editDeliveryTemplate").html()),
model: Deliveries.create()
}).render();
this.$el.append(editDeliveryForm.el);
$("#triggerEditDelivery").fancybox({
'afterClose': function () {
commitForm(editDeliveryForm);
//Wait do display the inlineModel until here
// Once we've bound the form to the model, put the saving logic with the collection
//Deliveries.last().save();
}
}).trigger('click');
},
// Specify a jQuery selector to put the itemView instances in to
itemViewContainer: "#deliveries",
});
EDIT
Thanks to #ejosafat! Had to destroy the model instead of just removing from collection.
removeDeliveryOption: function() {
this.model.destroy();
}
The remove method only affects the collection loaded in the browser, not in the permanent storage (local or server). That's why it dissappears from the view but when you reload the page it appears again.
If you want to get rid of that model in the storage too, use its destroy method.
(btw, it's a common convention in Javascript to use initial capital letter only for constructor functions, as clue that it should be used with the new operator, or be extended to create a derived constructor/class, so it's a bad idea to use Deliveries as a collection var name)

Categories

Resources