Knockout-Validation
Knockout-Validation copied to clipboard
ko.validation.group performance hit
We have a view model with very few observables, and only 1 that has validation, it has a observableArray with ~90 items, it also as very few observables and one with validation so we have 90 + 1 that needs to be tracked by the validation framework.
ko.validation.group(this) takes 350 ms in firefox / chrome and in IE7 the script wont complete, it hangs and says that the script didnt respond
Looks like there are serious performance problems with ko.validation.group, i need it to disable save buttons and such
Thanks, Anders
@AndersMalmgren
The 'group' function works recursively. Are you using the 'deep' option? Also, could you provide a jsfiddle or example code so that I can see what exactly it is that you are doing?
Hello Eric,
Using the options { deep: true, observable: false }, and calling isValid from a computed function triggers the performance problems for me.
Best, Camille
I would suggest you put some debug in the ko.validation.group function and just see how many observables are being traversed to create the group. These functions that go off doing unbounded traversal of object structures are incredibly dangerous to use even on a small scale.
I'd like to know if anything has been done or investigate in the last 8 months about this issue. We are currently running in the exact same situation where everytime that function is call, our whole SPA freeze out until this function has done his job.
Is there anything to do about it? I understand that it's a recursive function and depend on the number of objets to traverse is can become overwhelming, but there must be a way to optimize that function so I mitigate the problem...
I'll have a look at it to see if I can do something about it, but hey, if anyone has an idea, you're welcome!
@maroy1986 do you use the options { deep: true, observable: false }
? If it is set to false then it has to retraverse your object model to calculate the answer and that is a very expensive option (as written by @tuscland).
But it shouldn't be more expensive than ordo N, which it clearly is because its ultra slow. I did a concurrency framework that also iterates the model and its very fast on iterating a large model.
@delixfe I actually tried many combination of configurations with the deep and observable options. What happen is that I made a function in my script that check if the page is valid (it's typescript with durandal) :
pageIsValid: ko.computed(() => {
if (router.activeItem() != null)
{
return ko.utils.unwrapObservable(ko.validation.group(router.activeItem(), { deep: true, observable: true })).length < 1
}
})
If I put the deep function to false, it always return true, so it stop working. I'm using that function to disable the submit button on the form. I didn't found any built-in function in the library that allow me to do this so I made that custom one. If there's one, I missed it when I looked at it so maybe you could tell me.
If I remove any call to that function, everything is actually working great so it might be my fault, not KoV fault, but I still believe that some optimization should be done in this particular area after doing some profiling on it this week end.
@maroy1986 I think the problem is that you are always recreating the validation group. That is an expensive operation. You should create the validation group and cache it (in plain JavaScript):
var self = this;
self.errors = ko.validation.group(router.activeItem, { deep: true, observable: true });
self.pageIsValid = ko.computed(function() {
return self.errors().length = 0;
});
I can confirm from experience working on a large codebase involving knockout that developers with some knowledge of the basics always recreate their validation groups and that it's a real performance trap. It'd be really good if ko-validation cached it itself so you could request it as often as you liked and had a separate, more expensive sounding function to recreate the group from scratch.
On 29 April 2013 23:50, delixfe [email protected] wrote:
@maroy1986 https://github.com/maroy1986 I think the problem is that you are always recreating the validation group. That is an expensive operation. You should create the validation group and cache it (in plain JavaScript):
var self = this; self.errors = ko.validation.group(router.activeItem, { deep: true, observable: true }); self.pageIsValid = ko.computed(function() { return self.errors().length = 0; });
— Reply to this email directly or view it on GitHubhttps://github.com/Knockout-Contrib/Knockout-Validation/issues/73#issuecomment-17167629 .
@damonsmith Could you explain the use cases for always recreating the validation groups?
sorry I don't think I was clear enough. Developers here recreate validation groups but they don't have a valid use case for doing so, it's just that from the available methods and a small amount of knowledge, they thought that you call ko.validation.group every time, and so they did so and the code worked and so they never though about it again.
And in case you do want a use case for reuse and recreation of validation groups: Our app is really straight forward: we have forms for submitting info, we have various validations on a number of fields. We then have a submit button that needs to check that all fields are valid before submitting. There are not loads of fields on our forms, maybe 10 or 15, but there are lots of observables in the model object structure.
In the submit methods of some of our form components, developers have found the ko.validation.group function and called it on the model object before submitting, which causes ko.validation to recreate the whole validation group each time you click submit. Then once that precedent has been set, other methods that need to check model validity have cut and pasted versions of the same thing, all of a sudden when you have a list of model objects and you want to show validation errors on any of them, you are doing major amounts of compute for a fairly trivial job.
For recreating validation groups: We also have forms with different sections depending on other field states. Think a country dropdown where for each country there is a different set of contact detail fields. In this case we DO want to recreate the validation group whenever country changes. So that is where we would call a generateValidationGroup method.
As you might expect, once I found the issue with a profiler I had to go back through and store all the validation groups, which was difficult to do once all the code was working.
On 30 April 2013 15:21, delixfe [email protected] wrote:
@damonsmith https://github.com/damonsmith Could you explain the use cases for always recreating the validation groups?
— Reply to this email directly or view it on GitHubhttps://github.com/Knockout-Contrib/Knockout-Validation/issues/73#issuecomment-17210029 .
Here is a typical example why reusing a group does not work
http://jsfiddle.net/LrzFD/
It still slow making the initial load for a collection of 30+ elements slow
We finally found a solution to it, but it doesn't involve any modification of KnockoutValidation.
As you seen in the code above, we where passing the entire page model (router.activeItem()) to knockout validation, which was causing the entire model to be parsed by KnockoutValidation. Useless to say that about 95% of the stuff inside that wasn't realy necessary which cause the lag...
Instead, we go with a lighter solution. In each of our ViewModel that need validation, we've had a property called itemsToValidate, which is an array. Then, when we have a page that require validation, for exemple a user profile, we pass only the models we wan't to be validate, so for exemple our user profile is composed of a Profile object, so basicaly the only thing we do is.
//Only the profile
itemsToValidate = [Profile]
//You can also add as many as you want
itemsToValidate = [Profile,foo,bar,etc]
And the in we replaced the router.activeItem() in the group function by router.activeItem().itemsToValidate. Voila! It's magic and very fast again!
Hope it can help :)
@maroy1986 Thanks, this solution did work for me.
@maroy1986 This worked for me too! Thank you!