Closed comerc closed 8 years ago
I think it's about time for this. Here's what I'm thinking to do regarding schemas and validations:
Template.Adult.viewmodel({
name: '', // property is infered to be ViewModel.property.text
age: ViewModel.property.number.min(18).default(18), // default value provided
email: ViewModel.property.email.required, // email defaults to ''
password: ViewModel.property.text.min(10).max(50).required, // text defaults to ''
cards: 52, // property is infered to be ViewModel.property.number
// Should the view model accept properties that aren't part of the initial specification?
// Default: false
acceptNewProperties: true
})
// Set acceptNewProperties for all view models:
ViewModel.acceptNewProperties = true;
So in the case of the phone book example, the login view model would be simplified like this:
Template.login.viewmodel({
mixin: 'email',
isNew: true,
signHover: false,
signText: function() {
return this.isNew() ? 'Sign Up' : 'Sign In';
},
name: ViewModel.property
.text
.message('Name is required')
.validate(() => { !this.isNew() || !!this.name() }),
email: ViewModel.property.email.message('Valid email is required'),
password: ViewModel.property
.text
.message('Password must be at least 8 characters long')
.required
.min(8),
enter: function() {
if (! this.valid()) return;
if (this.isNew()){
Accounts.createUser({
email: this.email(),
password: this.password(),
profile: {
name: this.name()
}
}, function(err){
if (err) {
toastr.error("Could not create your user:<br>" + err.reason);
}
})
} else {
Meteor.loginWithPassword(this.email(), this.password(), function(err){
if (err) {
toastr.error("Could not log you in:<br>" + err.reason);
}
});
}
}
});
I'm also thinking of adding a few extra stuff like:
prop: ViewModel.property
.beforeUpdate( (newValue) => { } )
.afterUpdate( (oldValue) => { } )
.throttle(500)
Yes! I want to suggest more extra stuff from SimpleSchema Rules: decimal, exclusiveMin/exclusiveMax, minCount/maxCount, allowedValues, regEx, trim, autoValue.
What's the difference between decimal and number? What on earth is exclusiveMin/Max? (I read the docs and I still have no clue)
decimal(false) - is integer, but decimal(true) - is float exclusiveMin/Max - allow value wo range: a < exclusiveMin || a > exclusiveMax
No decimal. Just number
and integer
.
I still have no clue what exclusiveMin/Max is. Can you give a real example?
Example: parking is prohibited from 8:00 to 23:00
That's an odd way of saying between and notBetween. I'll include the functionality tho (with a different name of course). On Apr 21, 2016 3:49 PM, "comerc" notifications@github.com wrote:
Example: parking is prohibited from 8:00 to 23:00
— You are receiving this because you commented. Reply to this email directly or view it on GitHub https://github.com/ManuelDeLeon/viewmodel/issues/236#issuecomment-213126901
We need validation rules in model-level. Yes! SimpleSchema provide them. But separate validation rules in ViewModel level - it is overhead. No? May be best way is mapping ViewModel props with SimpleSchema fields.
No way I'll add a schema package as a dependency of ViewModel. Even if I thought it was a good idea to include the dependency, SimpleSchema deals with DB models while this library deals with VIEW models. They're different and you shouldn't conflate the two.
Astronomy provides a complete solution for schema, validation and other data related operations. Using them in VM template is easy and clean. I am not too sure about the value of adding schema/validation to VM as it should focus on View. I'd suggest to make it an optional package if it's implemented.
OK. We need plugin-mechanism for validation from external model-package. And we need mapping of ViewModel.props with Model.fields - one for two cases: for use validation and save data.
Instead of:
var contact = {
name: this.name(),
number: this.number(),
email: this.email(),
categoryId: this.categoryId()
};
Template.Adult.viewmodel({
age: this.mapTo('adult.age'),
// where 'adult.age' is model.field name
validateCallback: function(fieldName, value) { return true; },
// fieldName is 'adult.age', value is this.age()
modelDataCallback: function(fieldName, value) { return value; }
// it allow transform value if necessary
});
And then this.getModelData()
return data for save to Model from mapped props.
@daveeel
I'm not talking about data/entities schemas. I want to add "view" validations which may or may not relate to your application's entities. For example, I've always found the following piece of code a bit awkward:
message: '',
messageValid() {
return !!this.message() && this.message().length > 8;
},
logMessageIfValid() {
if ( this.messageValid() ) console.log( this.message() );
}
Remember that there may or may not be a "messages" collection in your data store. I think it's much cleaner to do:
message: ViewModel.property.text.min(8),
logMessageIfValid() {
if ( this.message.valid() ) console.log( this.message() );
}
Now I see what you mean. Agreed !
Implementation of validation with SimpleSchema:
Nodes = new (Mongo.Collection)('nodes')
NodeSchema = new SimpleSchema
fieldHead:
type: String
max: 3
fieldBody:
type: String
min: 3
Nodes.attachSchema(NodeSchema)
Template.hello.viewmodelEvents =
'submit form': ->
data = @getModelData()
result = validationContext.validate(data)
console.log 'submit', data, result
return false
validationContext = Nodes.simpleSchema().newContext()
map =
'propHead': 'fieldHead'
'propBody': 'fieldBody'
Template.hello.viewmodel
propHead: ''
propBody: ''
validateCallback: (fieldName, value) ->
obj = {}
obj[fieldName] = value
return validationContext.validateOne(obj, fieldName)
validate: (propName) ->
fieldName = map[propName]
return @validateCallback(fieldName, @[propName]())
modelDataCallback: (fieldName, value) ->
return value
getModelData: ->
result = {}
for propName, fieldName of map
result[fieldName] = @modelDataCallback(fieldName, @[propName]())
return result
events: Template.hello.viewmodelEvents
ViewModel.helperName = 'b'
template(name="hello")
form
input($b="value: propHead, class: { error: !validate('propHead') }")
input($b="value: propBody, class: { error: !validate('propBody') }")
button(type="submit") Submit
What do you think?
OK. It is not true. We need validate prop when set value and store validation state.
@comerc Once VM validations comes out that whole thing will be reduced to:
Template.hello.viewmodel
propHead: ViewModel.property.text.max(3)
propBody: ViewModel.property.text.min(3)
submit: ->
data = this.data()
result = this.valid()
console.log 'submit', data, result
template(name="hello")
form
input($b="value: propHead, class: { error: !propHead.valid }")
input($b="value: propBody, class: { error: !propBody.valid }")
button($b="click: submit") Submit
Once again, ViewModel validations and SimpleSchema validations have different concerns. One deals with views and the other with dbs.
Validation must not depend of ViewModel. I found, where I need callback for realization external validation. It is named setValueCallback
. Please waiting. I write demo.
viewmodel.coffee
setValue = (value, container, bindValue, viewmodel) ->
# console.log value, container, bindValue, viewmodel
if dotRegex.test(bindValue)
i = bindValue.search(dotRegex)
i += 1 if bindValue.charAt(i) isnt '.'
newContainer = getValue container, bindValue.substring(0, i), viewmodel
newBindValue = bindValue.substring(i + 1)
setValue value, newContainer, newBindValue, viewmodel
else
# !!!
container.setValueCallback(bindValue, value) if container.setValueCallback?
if _.isFunction(container[bindValue]) then container[bindValue](value) else container[bindValue] = value
return
I have no clue of what you mean. On Apr 24, 2016 3:33 PM, "comerc" notifications@github.com wrote:
Validation must not depend of ViewModel. I found, where I need callback for realization external validation. It is named setValueCallback. Please waiting. I write demo.
viewmodel.coffee
setValue = (value, container, bindValue, viewmodel) ->
console.log value, container, bindValue, viewmodel
if dotRegex.test(bindValue) i = bindValue.search(dotRegex) i += 1 if bindValue.charAt(i) isnt '.' newContainer = getValue container, bindValue.substring(0, i), viewmodel newBindValue = bindValue.substring(i + 1) setValue value, newContainer, newBindValue, viewmodel else container.setValueCallback(bindValue, value) if container.setValueCallback? if _.isFunction(container[bindValue]) then container[bindValue](value) else container[bindValue] = value return
— You are receiving this because you commented. Reply to this email directly or view it on GitHub https://github.com/ManuelDeLeon/viewmodel/issues/236#issuecomment-214041429
Lol
OK, I am released TemplateTwoWayBinding with required features. :)
Great job with 4.1.0! Validations working great so far! Many thanks!
Lol, I didn't announce it because I screwed up the documentation site. I'm glad you like it. On May 5, 2016 10:29 PM, "hluz" notifications@github.com wrote:
Great job with 4.1.0! Validations working great so far! Many thanks!
— You are receiving this because you commented. Reply to this email directly or view it on GitHub https://github.com/ManuelDeLeon/viewmodel/issues/236#issuecomment-217345721
Noticed that... Thanks god I "grabbed" the docs while it was up and had it running for a while in one browser tab... Unfortunately though, I pressed the browser back key while on that tab and when trying to go forward again got the server error... :-(
It's working now (knock wood). I wanted to include the React part of the docs but in my scrambling left it out.
On Thu, May 5, 2016 at 11:23 PM, hluz notifications@github.com wrote:
Noticed that... Thanks god I "grabbed" the docs while it was up and had it running for a while in one browser tab... Unfortunately though, I pressed the browser back key while on that tab and when trying to go forward again got the server error... :-(
— You are receiving this because you commented. Reply to this email directly or view it on GitHub https://github.com/ManuelDeLeon/viewmodel/issues/236#issuecomment-217350721
.validate(function(value){...})
multiple times can indeed make code cleaner... specially if each one could set its own invalidMessage('xx')
... hint,hint... ;-)
That would explode the API. Every validation would then have to support adding a valid/invalid message. I think the validation API is already bloated so I'll leave it as it is for now.
Validations are in 4.1.0: https://viewmodel.org/docs/viewmodels#validation
And with that, I close this ticket.
key features
- compatible with Blaze.Template
- minimum changes for migration your great project to template2
- one time declaration of variables to model - inside input fields attribute
- validate input data and get form data without excess coding
- support of model type as SimpleSchema
- may be extended for support any other model type (Astronomy etc.)
head
title simple
body
+demo(test='123')
template(name="demo")
div props:
= props.test
div
a.node(href="#") NEW
form
input(type="text" value-bind="fieldHead|throttle:500")
= state.fieldHead
input(type="text" value-bind="fieldBody|debounce:500")
= state.fieldBody
button(type="submit") SUBMIT
button#reset RESET
= state.submitMessage
= state.errorMessages
ul
+each node in nodes
li
a.node(href="#" data-node-id=node._id)
= node.fieldHead
| /
= node.fieldBody
Template.demo.onCreated ->
@propsSchema new SimpleSchema(test: type: String)
@modelSchema Nodes.simpleSchema()
@states
nodeId: false
submitMessage: ''
@helpers
nodes: ->
Nodes.find()
@events
'click a.node': (e) ->
e.preventDefault()
@state.nodeId = $(e.target).data('node-id') or false
'submit form': (e) ->
e.preventDefault()
@viewDoc (error, doc) ->
return if error
# save data
if @state.nodeId
Nodes.update @state.nodeId, $set: doc,
=> @state.submitMessage = 'updated'
else
@state.nodeId = Nodes.insert doc,
=> @state.submitMessage = 'inserted'
# old school of declaration with context of Template.instance()
Template.demo.eventsByInstance
'click #reset': (e) ->
e.preventDefault()
@modelDoc false
@state.submitMessage = ''
Template.demo.onRendered ->
@modelMap() # magic here :)
@autorun =>
if @state.nodeId
@modelDoc Nodes.findOne @state.nodeId
else
@modelDoc false
@state.submitMessage = ''
@Nodes = new (Mongo.Collection)('nodes')
@NodeSchema = new SimpleSchema
fieldHead:
type: String
label: 'My Head'
max: 3
defaultValue: '111'
fieldBody:
type: String
label: 'My Body'
min: 3
defaultValue: '777'
Nodes.attachSchema(NodeSchema)
Example: