I have a viewmodel and there I have properties which are extended to use validation. I call ko.validation.group(self) but this doesn't add the isValid() method to the viewmodel.
So I get an error that isValid() is undefined.
Here is my code:
var brechtbaekelandt = brechtbaekelandt || {};
brechtbaekelandt.login = (function ($, jQuery, ko, undefined) {
"use strict";
function LoginViewModel() {
var self = this;
self.userName = ko.observable();
self.password = ko.observable();
self.rememberMe = ko.observable();
self.errorMessage = ko.observable();
self.userName.extend({ required: { message: 'Please enter your username' } });
self.password.extend({ required: { message: 'Please enter your password' } });
self.errors = ko.validation.group(self);
};
LoginViewModel.prototype.login = function () {
var self = this;
self.errorMessage(null);
alert('entering login');
// self.isValid() is not a function
if (!self.isValid()) {
alert('login invalid');
self.errors.showAllMessages();
return;
}
else
{
alert('login valid');
// do login
}
};
function init() {
alert('entering init');
var knockoutValidationSettings = {
insertMessages: false,
decorateElement: true,
decorateElementOnModified: true,
decorateInputElement: true,
//errorMessageClass: 'error',
//errorElementClass: 'error',
//errorClass: 'error',
errorsAsTitle: false,
parseInputAttributes: false,
messagesOnModified: true,
messageTemplate: null,
grouping: { deep: true, observable: true }
};
ko.validation.init(knockoutValidationSettings, true);
var viewModel = new LoginViewModel();
ko.applyBindingsWithValidation(viewModel);
}
return {
LoginViewModel: LoginViewModel,
init: init
};
})($, jQuery, ko);
I have created a js fiddle: click here
I've read somewhere that you need to call registerExtenders() but I tried it and it doesn't work either.
Can someone help me in the right direction? Thx!
well you seem to be looking for isValid when using group tough there is a way (alternate way) using length property to achieve it . As isValid doesn't seem to be available when using group (it exists with validatedObservable) .
As #jeff mentioned in one of this answers on this topic
The ko.validation.group just gives you an (computed) observable of all
the error messages in a model. It only collects error messages of
direct properties of the model.
The ko.validatedObservable on the other hand not only collects the
error messages, but also wraps the model in an observable and adds an
isValid property which indicates whether or not there are any error
messages (i.e., the model was completely valid). Otherwise, they're
essentially the same.
I modified your code accordingly like below
self.errors = ko.validation.group(self); //It will group error messages in array i.e based on count you must validate
LoginViewModel.prototype.login = function () {
var self = this;
self.errorMessage(null);
//self.isValid() doesn't exist here . so you should base length
if (self.errors().length>0) {
alert('login invalid');
self.errors.showAllMessages();
return;
}
};
working sample with group
working sample with ValidatedObservable Preferable way Imho
Related
I'm passing a value as a parameter to a component.
<badge-button params="badge: oarBadge"></badge-button>
Here is the viewModel containing oarBadge:
function AppViewModel() {
var self = this;
self.oarBadge = ko.observable();
$.getJSON('/guy.json', function(data) {
var badge = new Badge('wood oar', data.badges.oar, false);
self.oarBadge(badge);
// self.oarBadge().has() returns true so the badge is being properly created with data
// returned by the ajax call
});
} // AppViewModel()
Here is the Badge constructor:
function Badge(name, has, active) {
var self = this;
self.name = ko.observable(name);
self.has = ko.observable(has);
self.active = ko.observable(active);
self.disabled = ko.computed(function() {
return self.has();
});
self.toggleActive = function() {
self.active(!self.active())
};
self.toggleHas = function() {
self.has(!self.has());
};
}
Here is the component's viewModel:
ko.components.register('badge-button', {
viewModel: function(params) {
var self = this;
self.badge = params.badge();
self.open = function() {
self.badge.toggleHas();
self.badge.toggleActive();
}
},
template:
'<img class="ui image" src="http://fakeimg.pl/300/" data-bind="click: open, css: { disabled: badge.disabled }" >'
});
When the page loads, I get an error telling me that badge is undefined.
Full example: https://gist.github.com/guyjacks/5a8763ff71f90e3fe8b4b153ed9a5283
Try setting a default object before the ajax call is completed, also you should assign the observable itself not the evaluation for the observable, so instead of doing this:
self.badge = params.badge();
You should do it like this:
self.badge = params.badge;
Otherwise your variable won't be updated once the ajax request is completed.
Here is a small example: https://jsfiddle.net/b0bdru1u/1/
Note: As far as I know the disable binding won't work in images
I'm looking to execute the Handsontable validation on the click of a button instead of on cell change. Something like this: validateCells() (return bool isValid). This function doesn't seem to be working for me.
var
data = [],
container = document.getElementById('hot-Handsontable'),
save = document.getElementById('save'),
hidden = document.getElementById('hot-Handsontable-value'),
hot,
hotIsValid = true,
emailValidator;
emptyValidator = function(value, callback) {
callback(false);
};
hot = Handsontable(container, {
data: data,
minRows: 1,
minCols: 21,
maxCols: 21,
minSpareRows: 1,
stretchH: 'all',
colHeaders: ['Test'],
columns: [{data:'Test',allowInvalid:true, validator: emptyValidator}]
});
// exclude empty rows from validation
$('.title-block .united-alert a[href^=#Handsontable]').click(function() {
var href = $(this).attr('href');
var row = href.getIdIndex();
var prop = /([^__]*)$/.exec(href)[0];
hot.selectCellByProp(parseInt(row), prop);
return false;
});
// Save event
Handsontable.Dom.addEvent(save, 'click', function(e) {
var test = hot.validateCells(); // test is undefined
if (hotIsValid === true) {
hidden.value = JSON.stringify(hot.getData());
} else {
e.preventDefault();
}
});
What you should be doing, instead of var test = hot.validateCells() is the following:
// Save event
Handsontable.Dom.addEvent(save, 'click', function(e) {
hot.validateCells(function(hotIsValid) {
if (hotIsValid === true) {
hidden.value = JSON.stringify(hot.getData());
} else {
e.preventDefault();
}
})
});
Notice that validateCells takes a callback and returns undefined. This is why you see test as undefined. One other thing to note is that the callback is executed for every cell in your table so be careful with it.
I have a list of validatedObservables defined. For instance one of them:
self.CompanyName = ko.observable().extend({ required: true });
self.ContactPerson = ko.observable().extend({ required: true });
self.step1Validation = ko.validatedObservable([
self.CompanyName,
self.ContactPerson
]);
I have also other validators except "required" one. For instance, email validator. When user enters incorrect email and moves to another field, then red error message appears. This means that error message are generated and appear nearby controls.
However, when I try to validate all validatedObservable at once, then error message doesn't appear nearby controls. How to fix this?
The validation looks like this (this is self function):
if (self.step1Validation.isValid()) {
return true;
} else {
// SHOULD SOMEHOW SHOW ALL ERROR MESSAGE FOR THIS STEP (step1Validation)
return false; // this doens't allow user to move to next step in wizard
}
EDIT
Here is some simplified jsfiddler example: http://jsfiddle.net/ng73s0hq/1/
In this example, if you add incorrect email and move to another field, then you can see "red error message". But if you press "submit" then validation fails, but there is no error messages (should be required failure + email validation failure).
All you need to do is on click you should call this self.step1Validation.errors.showAllMessages() to show error messages .
Simplified version viewModel:
ko.validation.init({
insertMessages: false
});
var patterns = {
email: /^([\d\w-\.]+#([\d\w-]+\.)+[\w]{2,4})?$/
};
var ViewModel = function () {
var self = this;
self.CompanyName = ko.observable();
self.ContactPerson = ko.observable();
self.Email = ko.observable();
self.test = ko.observable();
self.step1Validation = ko.validatedObservable([
self.CompanyName.extend({
required: true
}),
self.ContactPerson.extend({
required: true
}),
self.Email.extend({
required: true,
pattern: {
message: 'Must be a valid email',
params: patterns.email
}
})]);
self.clickDone = function () {
if (self.step1Validation.isValid()) {
self.test('valid');
return true;
} else {
self.test('invalid');
self.step1Validation.errors.showAllMessages()
return false;
};
};
};
var model = new ViewModel();
ko.applyBindings(model);
working sample here
You need to first validate your ko.validatedObservable object, and if invalid then use the showAllMessages(true) method to display validation errors.
var result = ko.validation.group(self.step1Validation, {deep: true});
if (self.step1Validation.isValid()) {
return true;
} else {
result.showAllMessages(true);
// SHOULD SOMEHOW SHOW ALL ERROR MESSAGE FOR THIS STEP (step1Validation)
return false; // this doens't allow user to move to next step in wizard
}
I am rather new to backbone and wanted to test a simple script that handles a to do list. Here is the code i used so far:
(function() {
window.App = {
Models: {},
Collections: {},
Views: {}
};
window.template = function(id) {
return _.template($('#' + id).html());
}
App.Models.Task = Backbone.Model.extend({
validate: function(attributes) {
if ( !$.trim(attributes.title) ) {
return 'Invalid title';
}
}
});
App.Collections.Tasks = Backbone.Collection.extend({
model: App.Models.Task
});
App.Views.Task = Backbone.View.extend({
tagName: 'li',
template: template('taskTemplate'),
initialize: function () {
this.model.on('change', this.render, this);
this.model.on('destroy', this.remove, this);
},
events: {
'click .edit': 'editTask',
'click .delete': 'destroy'
},
destroy: function() {
if (confirm('Are you sure?')) {
this.model.destroy();
}
},
remove: function() {
this.$el.remove();
},
editTask: function() {
var newTaskTitle = prompt('New title:', this.model.get('title'));
this.model.set('title', newTaskTitle, {validate: true});
},
render: function() {
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
App.Views.AddTask = Backbone.View.extend({
el: 'form#addTask',
initialize: function() {
},
events: {
'submit': 'submit'
},
submit: function(event) {
event.preventDefault();
var newTaskTitle = $(event.currentTarget).find('input[type=text]').val();
var task = new App.Models.Task({ title: newTaskTitle });
this.collection.add(task, {add: true, merge: false, remove: false});
}
});
App.Views.Tasks = Backbone.View.extend({
tagName: 'ul',
initialize: function() {
this.collection.on('add', this.addOne, this);
},
render: function() {
this.collection.each(this.addOne, this);
return this;
},
addOne: function(task) {
var taskView = new App.Views.Task({ model: task });
this.$el.append(taskView.render().el);
}
});
var tasks = new App.Collections.Tasks([
{
title: 'Go to store',
priority: 4
},
{
title: 'Go to mall',
priority: 3
},
{
title: 'Get to work',
priority: 5
}
]);
var addTaskView = new App.Views.AddTask({ collection: tasks });
var tasksView = new App.Views.Tasks({ collection: tasks });
$('div.tasks').append(tasksView.render().el);
})();
So the model validation works fine ... the only pb is that collection.add does not validate the newly added model .... is the a way to force the validation?
Thanks,
Rares
From the fine manual:
validate model.validate(attributes, options)
[...] By default validate is called before save, but can also be
called before set if {validate:true} is passed.
Collection#add does not call save nor does it call set with the validate: true option. If you want to validate during add, say so:
collection.add(models, { validate: true });
That will get validate:true all that way down to Model#set.
A quick look at a simplified example may be helpful:
var M = Backbone.Model.extend({
set: function() {
console.log('setting...');
Backbone.Model.prototype.set.apply(this, arguments);
},
validate: function() {
console.log('validating...');
return 'Never!';
}
});
var C = Backbone.Collection.extend({
model: M
});
var c = new C;
c.on('add', function() {
console.log('Added: ', arguments);
});
c.on('invalid', function() {
console.log('Error: ', arguments);
});
Now if we do this (http://jsfiddle.net/ambiguous/7NqPg/):
c.add(
{ where: 'is', pancakes: 'house?' },
{ validate: true }
);
You'll see that set is called with validate: true, validate will be called, and you'll get an error. But if you say this (http://jsfiddle.net/ambiguous/7b2mn/):
c.add(
{ where: 'is', pancakes: 'house?' },
{add: true, merge: false, remove: false} // Your options
);
You'll see that set is called without validate: true, validate will not be called, and the model will be added to the collection.
The above behavior is quite strongly implied but not explicitly specified so you may not want to trust it. Model#initialize does say:
you can pass in the initial values of the attributes, which will be set on the model.
and set does explicitly mention the validate option. However, there is no guarantee that Collection#add will send options to the model constructor or set or that the model's constructor will send the options to set. So if you want to be really paranoid and future proof, you could add a quick check for this "options get all the way down to set" behavior to your test suite; then, if it changes you'll know about it and you can fix it.
if you pass options to your collection add method, the validation method will not be called and as your arguments in this case are all set to the default value, there is not need to pass them
this.collection.add(task);
you may want to take a look at this question.
Prevent Backbone.js model from validating when first added to collection
I use the Knockout-Validation framework to validate the viewModel.
I have a viewmodel defined following:
ko.validation.init({
decorateElement:true,
errorElementClass: 'invalid',
insertMessages: false
});
var viewModel = ko.validatedObservable({
propety1: ko.observable().extend({ required: true }),
propety2: ko.computed(function () {
return this.propety1();
}, this),
form_onsubmit: function(form) {
console.log(this.propety1());
return false;
}
});
$(function () {
ko.applyBindings(viewModel);
});
it can get the property1's value in the form_onsubmit function, but it does't not work in the computed property "property2".
how to solve it, thanks!!!!!!!!!
When using object literals you need to define your computed properties separately:
var viewModel = ko.validatedObservable({
propety1: ko.observable().extend({ required: true }),
form_onsubmit: function(form) {
console.log(this.propety1());
return false;
}
});
viewModel().propety2 = ko.computed(function () {
return this.propety1();
}, viewModel());
Simply passing this as the second argument is not enough because it will refer to the global window object and not the object literal itself.