v-bind — хорош, особенно в случаях, когда нет такого перекрытия. Я не говорю, что он не подходит для этих целей.
Я о том, что наличие необходимости задумываться о взаимодействии двух способов задания пропсов — не самое лучшее дело, и иногда его использование приводит к неприятным ситуациям: например, разработчик, может добавлять или изменять поля в объекте — и по невнимательности — не заметить, что оно присутствует на компоненте в явном виде.
Это потенциальная проблема, которая может и не возникнуть. В то время как преимущества v-bind — вполне актуальны.
Я с вами согласен.
Я тоже не люблю состояние и предпочитаю чистые функции без побочных эффектов.
Но был выбор между двумя вариантами:
Валидатор возвращает объект с полным описанием проблемы(на подобие ValidationError), или null.
Валидатор возвращает true или false.
У первого решение — есть большие преимущества в виде фактической чистоты функций, отсутствия состояние и прочее и прочее.
У второго же варианта — есть минусы, такие как состояние, которое нужно где-то хранить, чтобы были возможны сообщения об ошибках и исправления.
Но мне валидация не выдаётся чем-то-то разтянутым по времени и месту. А значит что и хранимое состояние, и глобальные объекты — редко когда будут переживать по времени жизни — одну валидацию.
Приведу пару аргументов, почему я выбрал второй вариант:
1) Валидаторы можно использовать как предикаты без декораторов. Представим, что нам нужно свалидировать свойство во Vue:
export default {
props: {
messages: {
type: Array,
validator: v.arrayOf({ // Очень даже читаемо
author: v.and('not-empty', 'string'),
date: date => date instanceof Date,
text: v.and('not-empty', 'string')
})
}
}
// ...
}
Или использование в качестве аргумента в методы массива:
2) Происходит некоторое разделение обязанностей: валидатор — валидирует, объяснения объясняют, исправления исправляют
const instanceOf = constr => value => value instanceof constr
const isValidMessage = v({
author: v(
v.default(v.and('not-empty', 'string'), 'unknown'),
'author name is not valid'
),
date: v(
v.default(instanceOf(Date), new Date()),
'date is not a date'
),
message: v(
v.default('string', ''),
'message is not a string'
)
})
try {
let message = await apiRequest('someApiUrl')
v() // resetExplanation
if (!isValidMessage(message)) { // true or false
this.logError(new Error(v.explanation.join(', '))) // log error explanation
message = v.fix(message) // invalidObj => validObj
}
return message
}
C таким разделением функционала на части — код наглядно демонстрирует последовательность:
валидация
если не валидно — лог ошибки, и исправление
возвращение валидного результата
Я согласен, что возможно это не наилучшее решение — возможно его можно сделать ещё чище(в смысле меньшего кол-во глобальных вещей и чистоты функций). Подумаю над этим.
А зачем нужна такая гибкость? Я просто убежденный сторонник подхода «если не надо — выпиливаем» и «есть только один правильный способ сделать правильно», который меня уже много лет выручает.
Если не надо — выпиливаем — абсолютно согласен с этим подходом, равно как и с фразой есть только один правильный способ сделать это.
Дополнительный взгляд вверх по иерархии проверяемого объекта — вдохновлён отчасти методами массивов — которые позволяют при фильтрации — смотреть на значение массива. Например можно сделать валидатор, который по разному валидирует разные элементы массива: например элементы массива с индексами, которые являются простыми числами — должны быть простыми и тд.
Но такие случаи редки, и может даже не имеет смысла уделять им столько внимания. Чаще это всё же используется для организации разного рода дополнительного поведения: например, это используется методами исправляющими ошибки. В момент валидации валидаторы декорируемые методами-исправлениями (v.default, v.filterи v.addFix) строят дерево исправлений — там-то и используется путь от проверяемого объекта до исправляемого свойства.
Это дерево позже используется при вызове v.fix.
Но в общем случае, конечно, вы правы — решения давать больше возможностей могут приводить к возможностям стрелять себе в ногу. Подумаю как это можно улучшить.
Тогда не пришлось бы делать странные вещи вроде v.and.
Вы имеете в виду странность нейминга? Просто, вроде как, это та же функция, что и travajs/Compose.
v.explanation получается хранит глобальное состояние, это всегда боль.
Цель этой вещи собирать объяснения. Если нам понадобится их хранить — то они хранятся, если же нет — мы перед очередной проверки — очищаем этот список, с помощью v.resetExplanation() или короче v().
Обработав ошибки валидации — объяснения врядли нам нужны — поэтому то, что они будут стёрты перед следующей валидацией — не составляет проблемы.
Сейчас работает так, что не только композитор, но и сам валидатор — сам хранит объяснение, если таковое есть.
const isValidNumber = v('number', v => `'${v}' is not a number`)
const isValidString = v('string', v => `'${v}' is not a string`)
isValidNumber('not valid')
isValidString(123)
console.log(v.explanation) // => [`'not valid' is not a number`]
console.log(isValidNumber.explanation) // => [`'not valid' is not a number`]
console.log(isValidString.explanation) // => [`'123' is not a string`]
Так что нет ничего удивительного в том, что глобальный объект хранит глобальные объяснения, а локальные валидаторы — хранят свои объяснения. Но чаще всего просто легче использовать глобальное хранилище — в виду того, что туда идут все объяснения.
Не нашел удобным запись валидатора «снизу-вверх», мне кажется более логичным описывать «сверху-вниз» от родителя к потомкам.
Чаще всего так и будет и это приветствуется, дополнительные параметры — лишь добавляет некоторую гибкость:
v-bind
— хорош, особенно в случаях, когда нет такого перекрытия. Я не говорю, что он не подходит для этих целей.Я о том, что наличие необходимости задумываться о взаимодействии двух способов задания пропсов — не самое лучшее дело, и иногда его использование приводит к неприятным ситуациям: например, разработчик, может добавлять или изменять поля в объекте — и по невнимательности — не заметить, что оно присутствует на компоненте в явном виде.
Это потенциальная проблема, которая может и не возникнуть. В то время как преимущества
v-bind
— вполне актуальны.Я с вами согласен.
Справедливо, нужно было и этот вариант рассмотреть.
Мне он показался запутывающим в моменте, например:
В данном примере не очевидно, какое значение будет восприниматься как значение пропсы
firstName
На счет состояния не могу согласиться
Я тоже не люблю состояние и предпочитаю чистые функции без побочных эффектов.
Но был выбор между двумя вариантами:
null
.У первого решение — есть большие преимущества в виде фактической чистоты функций, отсутствия состояние и прочее и прочее.
У второго же варианта — есть минусы, такие как состояние, которое нужно где-то хранить, чтобы были возможны сообщения об ошибках и исправления.
Но мне валидация не выдаётся чем-то-то разтянутым по времени и месту. А значит что и хранимое состояние, и глобальные объекты — редко когда будут переживать по времени жизни — одну валидацию.
Приведу пару аргументов, почему я выбрал второй вариант:
1) Валидаторы можно использовать как предикаты без декораторов. Представим, что нам нужно свалидировать свойство во Vue:
Или использование в качестве аргумента в методы массива:
2) Происходит некоторое разделение обязанностей: валидатор — валидирует, объяснения объясняют, исправления исправляют
C таким разделением функционала на части — код наглядно демонстрирует последовательность:
Я согласен, что возможно это не наилучшее решение — возможно его можно сделать ещё чище(в смысле меньшего кол-во глобальных вещей и чистоты функций). Подумаю над этим.
А зачем нужна такая гибкость? Я просто убежденный сторонник подхода «если не надо — выпиливаем» и «есть только один правильный способ сделать правильно», который меня уже много лет выручает.
Если не надо — выпиливаем — абсолютно согласен с этим подходом, равно как и с фразой есть только один правильный способ сделать это.
Дополнительный взгляд вверх по иерархии проверяемого объекта — вдохновлён отчасти методами массивов — которые позволяют при фильтрации — смотреть на значение массива. Например можно сделать валидатор, который по разному валидирует разные элементы массива: например элементы массива с индексами, которые являются простыми числами — должны быть простыми и тд.
Но такие случаи редки, и может даже не имеет смысла уделять им столько внимания. Чаще это всё же используется для организации разного рода дополнительного поведения: например, это используется методами исправляющими ошибки. В момент валидации валидаторы декорируемые методами-исправлениями (
v.default
,v.filter
иv.addFix
) строят дерево исправлений — там-то и используется путь от проверяемого объекта до исправляемого свойства.Это дерево позже используется при вызове
v.fix
.Но в общем случае, конечно, вы правы — решения давать больше возможностей могут приводить к возможностям стрелять себе в ногу. Подумаю как это можно улучшить.
Честно говоря мне кажется что задача тривиальна.
Возможно, вы и правы
Для функций замечательно подходит функциональная композиция
Абсолютно согласен
не понятно зачем было притягивать объектную.
Это синтаксический сахар. То что можем записать так:
Можем записать и так:
Тогда не пришлось бы делать странные вещи вроде v.and.
Вы имеете в виду странность нейминга? Просто, вроде как, это та же функция, что и travajs/Compose.
v.explanation получается хранит глобальное состояние, это всегда боль.
Цель этой вещи собирать объяснения. Если нам понадобится их хранить — то они хранятся, если же нет — мы перед очередной проверки — очищаем этот список, с помощью
v.resetExplanation()
или корочеv()
.Обработав ошибки валидации — объяснения врядли нам нужны — поэтому то, что они будут стёрты перед следующей валидацией — не составляет проблемы.
Сейчас работает так, что не только композитор, но и сам валидатор — сам хранит объяснение, если таковое есть.
Так что нет ничего удивительного в том, что глобальный объект хранит глобальные объяснения, а локальные валидаторы — хранят свои объяснения. Но чаще всего просто легче использовать глобальное хранилище — в виду того, что туда идут все объяснения.
Не нашел удобным запись валидатора «снизу-вверх», мне кажется более логичным описывать «сверху-вниз» от родителя к потомкам.
Чаще всего так и будет и это приветствуется, дополнительные параметры — лишь добавляет некоторую гибкость:
Хотелось бы узнать, какие моменты показались сложными — возможно я не вижу каких-то более легких решений, если бы мне указали на них — был бы рад.