How do you use yup context in test() - yup

How do I check against values provided in the context-option for yup in the test()-function?
The documentation is clear on how to use context in when but it is less clear for test-functions.

Using runkit we can see that the context for the test i is buried under options in the test() context, i.e. it is not the same as for when():
var yup = require("yup")
const name = yup.string()
.label('A name of strings')
.test(
'not-taken',
'${path} is already already takem',
(value, { options: { context: { values } } }) => value !== values);
const options = { context: { values: 'Olof' }}
console.log(name.validateSync('Jimmy', options), 'validated')
console.log(name.validateSync('Olof', options), 'validated')
Still unclear is how the proper TypeScript declaration of context should be.

Related

AG Grid: Better way for validation row - valueSetter?

Is there a better way to validate a row in ag-grid than with valueSetter?
I can achieve the validation with that but I am not sure, if there is a better way.
https://www.ag-grid.com/javascript-grid-value-setters/#properties-for-setters-and-parsers
I want to validate two fields in the row. DateFrom and DateUntil (they are not allow to be null and DateFrom must be lower than DateUntil).
There are two ways of possible validation handling:
First: via ValueSetter function
and
Second: via custom cellEditor component
I suggest that it would be better to split the logic between custom components, but as you said you need to validate two cell-values between themselves.
On this case from UI perspective you could try to combine them inside one cell and it would be easily to work with values via one component only.
You could override the valueSetter and call the grid api transaction update instead.
Here is pseudo-code that shows how you could implement this.
valueSetter: params => {
validate(params.newValue, onSuccess, onFail);
return false;
};
validate = (newvalue, success, fail) => {
if (isValid(newValue)) {
success();
} else {
fail();
}
};
onSuccess = () => {
// do transaction update to update the cell with the new value
};
onFail = () => {
// update some meta data property that highlights the cell signalling that the value has failed to validate
};
This way you can also do asynchronous validation.
Here is a real example of an async value setter that has success, failure, and while validating handlers that do transaction updates when validation is done.
const asyncValidator = (
newValue,
validateFn,
onWhileValidating,
onSuccess,
_onFail
) => {
onWhileValidating();
setTimeout(function() {
if (validateFn(newValue)) {
onSuccess();
} else {
_onFail();
}
}, 1000);
};
const _onWhileValidating = params => () => {
let data = params.data;
let field = params.colDef.field;
data[field] = {
...data[field],
isValidating: true
};
params.api.applyTransaction({ update: [data] });
};
const _onSuccess = params => () => {
let data = params.data;
let field = params.colDef.field;
data[field] = {
...data[field],
isValidating: false,
lastValidation: true,
value: params.newValue
};
params.api.applyTransaction({ update: [data] });
};
const _onFail = params => () => {
let data = params.data;
let field = params.colDef.field;
data[field] = {
...data[field],
isValidating: false,
lastValidation: params.newValue
};
params.api.applyTransaction({ update: [data] });
};
const asyncValidateValueSetter = validateFn => params => {
asyncValidator(
params.newValue,
validateFn,
_onWhileValidating(params),
_onSuccess(params),
_onFail(params)
);
return false;
};
Here is a code runner example showing this in action: https://stackblitz.com/edit/async-validation-ag-grid-final
Have a look at this two snippets, these come from our internal knowledge base (accessible to customers)
When editing a value in column 'A (Required)', you will see that it does not allow you to leave it empty. If you leave it empty and return the edit, it will be cancelled.
//Force Cell to require a value when finished editing
https://plnkr.co/edit/GFgb4v7P8YCW1PxJwGTx?p=preview
In this example, we are using a Custom Cell Editor that will also validate the values against a 6 character length rule. While editing, if the value is modified outside of 6 characters, it will appear in red, and when you stop editing the row, the value would be reset, so it only accepts a complete edit if the value is valid.
//Inline Validation while editing a cell
https://plnkr.co/edit/dAAU8yLMnR8dm4vNEa9T?p=preview

How to replace function name with a babel plugin

Is it possible to create a babel plugin that will change some a functions name ?
I can't seems to find this in the documentation.
Example:
myObject.doSomething() ==> babel ==> myObject.___doSomething()
Thanks
You can get the AST of your code in astexplorer. And you can see it's about a CallExpression and MemberExpression. So search babel-types API in babel-types source code, it's very clear of how to create a babel type or judge a babel-type like this:
defineType("MemberExpression", {
builder: ["object", "property", "computed"],
visitor: ["object", "property"],
aliases: ["Expression", "LVal"],
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
});
Following are two different ways to do it (either with the Program visitor or with the FunctionDeclaration visitor):
export default function ({types: t}) {
return {
visitor: {
Program(path) {
path.scope.rename('doSomething', '___doSomething');
},
FunctionDeclaration(path) {
if (path.node.id.name === 'doSomething') {
path.node.id.name = '___doSomething'
}
}
}
};
}
Note that these are not safe since they can override an existing name. You can use the path.scope.generateUidIdentifier("uid"); command to generate a unique identifier and use that but you wont be able to define the generated name.
Example - http://astexplorer.net/#/o5NsNwV46z/1

Assign mongo selectors in find() dynamically

I have the following problem: I have an interface where a user can filter stuff out based on several inputs. There are 5 inputs. When an input is filled out I want to add it's value to the helper returning the collection. The problem I can't solve is how to do this dynamically. Sometimes the user might fill out one input, sometimes three, sometimes all 5. Within the find() method you can only write down meteor's syntax:
mongoSelector: fieldName,
This means you can only hardcode stuff within find(). But just adding all 5 selectors doesn't work, since if one of the values is empty, the find searches for an empty string instead of nothing.
I thought of doing conditionals or variables but both don't work within find because of the required syntax. What could I do to solve this?
var visitorName;
var visitorAge;
Session.set('visitorName', visitorName);
Session.set('visitorAge', visitorAgee);
Template.web.helpers({
visitors: function() {
return Visitors.find({ visitor_name: Session.get('visitorName'), visitor_age: Session.get('visitorAge') });
}
});
Template.web.events({
"change #visitor_name": function (event, template) {
visitorName = $(event.currentTarget).val();
}
});
Template.web.events({
"click #reset_filter": function (event, template) {
return Visitors.find();
$(input).val('');
}
});
http://jsfiddle.net/m5qxoh3b/
This one works
Template.web.helpers({
visitors: function() {
var query = {};
var visitorName = (Session.get('visitorName') || "").trim();
if (visitorName) {
query["visitor_name"] = visitorName;
}
//same thing for other fields
return Visitors.find(query);
}
});
Template.web.events({
"change #visitor_name": function (event, template) {
var visitorName = $(event.currentTarget).val();
Session.set('visitorName', visitorName);
}
});

Coffeescript best way to var

I was poking around trying to figure a good way to do var, and here's one.. is this intentionally in cs, because it's not documented?
me=;
fetchUser()
.then((_me) =>
me = _me
fetchFriend()
)
.then((_you) =>
me.friend(_you)
)
.then(done)
.otherwise(=>
console.log ':('
)
compiles correctly to
var me;
me = fetchUser().then((function(_this) {
return function(_me) {
me = _me;
return fetchFriend();
};
})(this)).then((function(_this) {
return function(_you) {
return me.friend(_you);
};
})(this)).then(done).otherwise((function(_this) {
return function() {
return console.log(':(');
};
})(this));
Edit: it's not compiling correctly.
I wouldn't expect me = fetchUser() either, but I didn't see that before
https://github.com/jashkenas/coffee-script/issues/3098
I think it's just a quirk of the parser. The normal way variables are declared in order to establish scoping is just by defining them with some default value (e.g. null).

Does Mongoose provide access to previous value of property in pre('save')?

I'd like to compare the new/incoming value of a property with the previous value of that property (what is currently saved in the db) within a pre('save') middleware.
Does Mongoose provide a facility for doing this?
The accepted answer works very nicely. An alternative syntax can also be used, with the setter inline with the Schema definition:
var Person = new mongoose.Schema({
name: {
type: String,
set: function(name) {
this._previousName = this.name;
return name;
}
});
Person.pre('save', function (next) {
var previousName = this._previousName;
if(someCondition) {
...
}
next();
});
Mongoose allows you to configure custom setters in which you do the comparison. pre('save') by itself won't give you what you need, but together:
schema.path('name').set(function (newVal) {
var originalVal = this.name;
if (someThing) {
this._customState = true;
}
});
schema.pre('save', function (next) {
if (this._customState) {
...
}
next();
})
I was looking for a solution to detect changes in any one of multiple fields. Since it looks like you can't create a setter for the full schema, I used a virtual property. I'm only updating records in a few places so this is a fairly efficient solution for that kind of situation:
Person.virtual('previousDoc').get(function() {
return this._previousDoc;
}).set(function(value) {
this._previousDoc = value;
});
Let's say your Person moves and you need to update his address:
const person = await Person.findOne({firstName: "John", lastName: "Doe"});
person.previousDoc = person.toObject(); // create a deep copy of the previous doc
person.address = "123 Stack Road";
person.city = "Overflow";
person.state = "CA";
person.save();
Then in your pre hooks, you would just need to reference properties of _previousDoc such as:
// fallback to empty object in case you don't always want to check the previous state
const previous = this._previousDoc || {};
if (this.address !== previous.address) {
// do something
}
// you could also assign custom properties to _previousDoc that are not in your schema to allow further customization
if (previous.userAddressChange) {
} else if (previous.adminAddressChange) {
}
Honestly, I tried the solutions posted here, but I had to create a function that would store the old values in an array, save the values, and then see the difference.
// Stores all of the old values of the instance into oldValues
const oldValues = {};
for (let key of Object.keys(input)) {
if (self[key] != undefined) {
oldValues[key] = self[key].toString();
}
// Saves the input values to the instance
self[key] = input[key];
}
yield self.save();
for (let key of Object.keys(newValues)) {
if (oldValues[key] != newValues[key]) {
// Do what you need to do
}
}
What I do is use this.constructor within the pre-save route to access the current value in the database.
const oldData = this.constructor.findById(this.id)
You can then grab the specific key you're looking for from the oldData to work with as you see fit :)
let name = oldData.name
Note that this works well for simple data such as strings, but I have found that it does not work well for subschema, as mongoose has built in functionality that runs first. Thus, sometimes your oldData will match your newData for a subschema. This can be resolved by giving it it's own pre-save route!