Feature
Current behavior is that validateNode only accepts the node undergoing validation as an argument.
I'm wondering how many other folks have found themselves wishing that they could access the value undergoing change. I have some complicated validation rules that depend on where a block sits in the document hierarchy, and this requires me to implement validations at the object: document level.
That's ok, but it would be a large optimization if I could have a validateNode rule that operated against a object: block and was able to analyze the block's parents. One nice way to achieve this might be to also pass in the Value object undergoing validation.
For example consider a validation where for some reason I only want to add a property to the data of certain blocks based on their position in the document and type.
// assume that there's some logic somewhere else that ensures this is only called on a document node
validateSomeBlocksHaveSpecialData(document) {
let invalids = document.nodes.filter(b => isSpecialType(b));
// lets say list items are special, but stuff inside of table cells are not, so it doesnt serve me to arbitrarily recurse
document.nodes.filter(b => isList(b)).forEach(li=> {
li.nodes.filter(c => isSpecialType(c)).forEach(child => {
invalids.push(child);
});
});
if (invalids.size) {
// return change function
}
}
This has to be run at the document level now because that's the only way to get an idea of the document hierarchy.
But what if I also had the value on hand? Then I could have validateNode functions that operate on the block level and look like
// assume there is some logic somewhere that ensures this is only called on a block node and block
// and value are basically passed down to this fn
validateSomeBlocksHaveSpecialData(block, value) {
// block is special if its not in a table
if(isSpecialType(b) && !value.document.getClosest(p => p.type !== 'table')) {
// return change function
}
}
I can then have a block validation function that operates against the block as it's modified, and I don't have to iterate over the entire document everytime something is changed
Is that a reasonable feature? Or have I wandered into some sort of antipattern?
Because the validateNode and decorateNode are called by the ancestor chain of the changed node, for examplecell->row->table->document
I would like to have ancestors instead of value as the optional argument (in both validateNode and decorateNode).
for the value; I remember we can return
change => ( const {value} = change;....}
, and use the default stack size to prevent un-stopped recursion.
Ancestors would definitely be sufficient for this example. But what if someone had some data attached to value that they needed? I guess it's kinda hard to find an example,
What about a rule that automatically numbered headers? We'd also need some idea of the sibling headers at the same "level" as well as the parents to figure out numbering.
@CameronAckermanSEL
about a rule that automatically numbered headers
sibling headerscan be found in by the children of the parent from the ancestors
Do you think it would be more efficient to pass out the value as opposed to always calculating the ancestors of the validated node, even if it might not be needed by the caller?
I believe this is currently impossible because of the architecture using to cache the normalization. We're only able to cache at the node level because we limit the normalization to only knowledge of the node, and not its parents.
Did some poking around. I'm definitely not sure if this would be a desirable thing, but it would be possible to get the value down to validateNode if we passed the value to n.validate(schema, value) here: https://github.com/ianstormtaylor/slate/blob/44eccca60a60908c88f022b1303452fc8467fde9/packages/slate/src/changes/with-schema.js#L145
And then passed it all the way down to schema.validateNode(node, value) to stack.find('validateNode', node, value) here: https://github.com/ianstormtaylor/slate/blob/44eccca60a60908c88f022b1303452fc8467fde9/packages/slate/src/models/schema.js#L262
Tests appear to pass and the core rules can access value (though they certainly don't need to). But yeah jury's still out on whether this is good behavior or not
Update: value cannot be easily passed down because it breaks memoization of the normalize functions.