Comments (7)
Hi Jose,
Ah yes, this one is technically not a bug but I can understand that it does not seem intuitive.
You may have noticed that Extra.test(input2) === true
. The difference between test
and validate
is that validate
expects a model instance, and does not provide any kind of automatic casting or duck typing. validate
is mainly used internally, it is called whenever a change is detected on your model instance.
TL;DR: test
tries to create a model instance from the object passed (and call validate
internally on the created instance) then return true
if no errors are catched.
The reason I exposed this validate
method is that some specific modifications are not detected (example: extending an array through an index assignment), so the user needs a way to manually call this validation step. In practice, it has very few usecases.
I will improve the API documentation to make things clearer
from objectmodel.
test - model.test(value)
Returns true if the value passed validates the model definition. Works with duck typing.
validate - model.validate(instance, [errorCollector])
Manually triggers the validation of a model instance over its definition. It is automatically done whenever an instance property changes.
Note that contrary to the test method, the object is not cast to its suitable model (a.k.a duck typing) before being validated.
better ?
from objectmodel.
Got you! Yep, I think that is clear enough.
The reason why we were using validate
is because it gives us a list instead of a single error. Using new Model()
gives you only one, and test
no one.
So our practical case is we get an external input that we want to validate as the first step and it could have more than one failed validation, so we want to return the complete list of errors.
Are we missing another exposed method to only set defaults to the argument? Something like Extra.applyDefaults(input2)
. Then the combination of that method and validate
will make the job.
Thanks for your time! Is really appreciated your quick responses!
from objectmodel.
Error collectors should collect all errors, not just the first one. Each error is displayed in a new line, maybe you missed the others ? See this example:
See Custom error collectors section for more info on how to collect errors as a list and do whatever you want with them.
To get what you want (cast + validation), calling the model constructor is indeed the best way to do it.
About applyDefaults
, remember that defaults
is just a helper to set things on the model prototype. So Extra.defaults({ amount: 0 });
is equivalent to Extra.prototype.amount = 0
.
Therefore setting the defaults means assigning the prototype. A way to do this and only apply defaults would be Object.setPrototype(input2, Extra.prototype)
. But I would recommend to always use the model constructor since it does additional controls and is the appropriate way to create model instances.
from objectmodel.
OK! you put me in the right path! Before we weren't using the errorCollector
when creating an instance.
So, this should be the best way to do it:
/**
* Validates the input against a Model.
* It returns an instance if valid. Or a list of errors if invalid.
* @param Model
* @param input
*/
static validate(Model, input) {
return new Promise((resolve, reject) => {
if (!(Model instanceof objectmodel)) {
return reject([new Error('Model should be an objectmodel class')]);
}
// before
// if (Model.test(input)) return resolve(new Model(input));
// return Model.validate(input, errors => reject(errors));
// after
const errorCollector = _.clone(Model.errorCollector); // backup
Model.errorCollector = errors => reject(errors); // catch errors
const instance = new Model(input);
if (instance) {
instance.errorCollector = errorCollector; // restore
return resolve(instance);
}
return null;
});
}
Does it sounds right to you?
Thanks!
from objectmodel.
You made a mistake here: instance.errorCollector = errorCollector; // restore
; errorCollector is a property of the model, not the instance
Note that object model validation is synchronous but I guess you want to return a Promise for your convenience. So I would do it this way:
static validate(Model, input) {
return new Promise((resolve, reject) => {
if (!(Model instanceof objectmodel)) {
return reject([new Error('Model should be an objectmodel class')]);
}
let errors = null;
const initialErrorCollector = Model.errorCollector;
Model.errorCollector = (errorsCatched) => { errors = errorsCatched; };
const instance = new Model(input);
Model.errorCollector = initialErrorCollector;
return errors === null ? resolve(instance) : reject(errors);
}
}
or with a try/catch block:
static validate(Model, input) {
return new Promise((resolve, reject) => {
if (!(Model instanceof objectmodel)) {
return reject(new Error('Model should be an objectmodel class'));
}
try {
const instance = new Model(input);
resolve(instance);
} catch(error){
reject(error);
}
}
}
Of course if you're planning to always validate your model instances this way, it would be easier to globally change the errorCollector on Model.prototype. I assumed you made this for a specific reason such as form input validation.
from objectmodel.
Yes, you are right. We want to change the behavior to initially get the list, but we want to roll back to the initial errorCollector
so we should get an exception if something is wrong.
The promise interface keeps our code more predictable as it's unified with the rest of validation methods (most of them asynchronous).
Your first example fits 100% what we are looking for, thanks so much!
from objectmodel.
Related Issues (20)
- Feature Request: Code Suggestion/IntelliSense on IDE (Jetbrain Webstorm) HOT 4
- Package objectmodel has been ignored because it contains invalid configuration HOT 2
- Problem Extending Typescript Class HOT 5
- Consider creating ADL serializer to generate ObjectModel? HOT 1
- How to nest SealedModels? HOT 8
- How to add custom methods to a Model? HOT 1
- Working with VSCode Intellisense HOT 2
- Question: Is it possible to skip more detailed checks if initial type check fails? HOT 6
- [email protected]: The engine "node" is incompatible with this module HOT 3
- Default for Nested Models HOT 1
- Load Json to Object HOT 1
- Model composition and defaults HOT 11
- Typescript expected number of arguments when using defaultTo() HOT 4
- get objectmodel added to typia benchmarks HOT 4
- Nice Project HOT 1
- How can i return a « cleaned » object ? HOT 2
- Use in Typescript ? HOT 2
- pre/post process some datas ? HOT 2
- I can't catch the exceptions HOT 2
- Proposal to reduce circular dependency headaches
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from objectmodel.