How do you pass in an existing document to update?
When I try to pass it in as a model - values update in the form _but_ validation no longer throws errors (I assume because it's not validating the new data??)
Actually it is validating new model. Could you share a not working example?
I'll close it for now. If problem persist, please let me know.
I am also experiencing this issue, when I use <AutoForm schema={userSchema} model={user} onSubmit={doc => console.log(doc)} > I get no validation at all, but when I do not include a model: <AutoForm schema={userSchema} onSubmit={doc => console.log(doc)} > It validates the form against the schema.
Could you share some more code, @todda00?
I was able to determine the issue is with a document which has collection helpers (https://github.com/dburles/meteor-collection-helpers) on it, providing prototype functions on the object.
The following is my user schema and collection helpers:
import { Meteor } from 'meteor/meteor';
import {SimpleSchema} from 'meteor/aldeed:simple-schema';
import AddressSchema from '/lib/collections/addressSchema';
import moment from 'moment';
const Guardian = new SimpleSchema({
firstName:{
type: String,
optional: true
},
lastName:{
type: String,
optional: true
},
phone:{
type: String,
optional: true,
min:10
},
email:{
type: String,
regEx: SimpleSchema.RegEx.Email,
optional:true
}
});
const schema = new SimpleSchema({
_id:{
type: String,
regEx: SimpleSchema.RegEx.Id
},
createdAt: {
type: Date
},
services: {
type: Object,
blackbox: true,
optional: true
},
roles:{
type: Object,
blackbox: true,
optional : true
},
emails:{
type: [Object]
},
'emails.$.address':{
type: String,
regEx: SimpleSchema.RegEx.Email
},
'emails.$.verified':{
type: Boolean
},
profile:{
type: Object
},
'profile.firstName':{
type: String
},
'profile.lastName':{
type: String
},
'profile.phone':{
type: String,
optional: true,
min:10
},
'profile.address':{
type: AddressSchema,
optional:true
},
'profile.gender':{
type: String,
allowedValues:['male','female'],
optional: true
},
'profile.birthdate':{
type: Date,
max (){
return moment().subtract(13,'years').toDate();
},
optional: true
},
'profile.primaryGuardian':{
type: Guardian,
optional:true
},
'profile.secondaryGuardian':{
type: Guardian,
optional:true
}
});
Meteor.users.attachSchema(schema);
Meteor.users.helpers({
fullName() {
return this.profile.firstName + ' ' + this.profile.lastName;
},
formatedBirthdate(format='MM/DD/YYYY'){
if (this.profile === undefined || this.profile.birthdate === undefined) return null;
return moment(this.profile.birthdate).format(format);
}
});
export default Meteor.users;
I am using Matra, here is my container:
import {useDeps, composeAll, composeWithTracker, compose} from 'mantra-core';
import Register from '../components/register.jsx';
import RegisterPlayerBasicInfo from '../components/register_player_basic_info.jsx';
import RegisterUserType from '../components/register_user_type.jsx';
import RegisterAccount from '../components/register_account.jsx';
export const composer = ({context, clearLocalState, step}, onData) => {
const {Meteor, Collections, LocalState} = context();
const userSchema = Meteor.users.simpleSchema();
const error = LocalState.get('error.user.register');
let topText, StepComponent, percentage = 0;
switch(step){
case 'RegisterUserType':
percentage = 0;
topText = "First, tell us what you're looking for.";
StepComponent = RegisterUserType;
break;
case 'RegisterAccount':
percentage = 0;
topText = "Great! Let's get started.";
StepComponent = RegisterAccount;
break;
case 'RegisterPlayerBasicInfo':
percentage = 25;
topText: "To get the best possible matches, complete your profile!",
StepComponent = RegisterPlayerBasicInfo;
break;
}
//Load the user into context if logged in
const uid = Meteor.userId();
if (Meteor.subscribe('users.single',uid).ready()){
const user = Meteor.users.findOne({_id:uid});
onData(null, {userSchema, error, topText, percentage, StepComponent, user});
}
//clear shared LocalStates when unmounting the component
return clearLocalState;
};
export const depsMapper = (context, actions) => ({
context: () => context,
clearLocalState: actions.register.clearLocalState,
// updateField: actions.register.updateField,
});
export default composeAll(
composeWithTracker(composer),
useDeps(depsMapper)
)(Register);
The Register component has some common elements, but nothing to do with this issue, so I will just show you the component which shows the form (In reality I am using a mix of custom and AutoField and other types, but using the autogenerated AutoForm has the same issue, so I will simplify my example):
import React from 'react';
import {AutoForm} from 'uniforms-bootstrap4';
class RegisterPlayerBasicInfo extends React.Component {
render(){
const {user, userSchema, error} = this.props;
return(
<div className='text-xs-left'>
<AutoForm schema={userSchema} model={user} onSubmit={doc => console.log(doc)} />
</div>
);
}
}
export default RegisterPlayerBasicInfo;
This all works when I remove the collection helpers. Here is the user object with collection helpers:

And here is the user object without collection helpers:

HA! yes indeed - I also use meteor-collection-helpers and that was likely the cause of the problem for me too...
It's interesting, but it seems that's a problem of SimpleSchema itself. I'll try to find out, what's going on.
Unfortunately _(is it?)_ it's a SimpleSchema problem. It simply does nothing with non-plain objects. There's also an issue for that. Unluckily, it's almost one year old.
I was able to get this to work if I convert my object to a basic object using this (uses lodash's forOwn):
const basicObj = {};
_.forOwn(user,(val,key)=>{
basicObj[key] = val;
});
Then using basicObj as the model to validate, it works. Would this be a bad idea to incorporate into this project, stripping out any model's inherited properties, since SimpleSchema will ignore the whole object otherwise?
You can simplify it - {...doc} will suffice.
I think it's not necessary because it will cause additional overhead if you are not using prototype-based documents.
I certainly think this is a _gotcha_ worth a bit of code in package, to avoid users having to run into issues trying to use the package... especially since, inside the package, it is basically only required when ingesting the initial model
getValidator (options = {clean: true}) {
const validator = this.schema.validator(options);
- return model => validator(cloneDeep(model));
+ return model => validator(cloneDeep({...model}));
}
This change in SimpleSchemaBridge should suffice - striping prototype is needed only during SimpleSchema validation.
Most helpful comment
This change in
SimpleSchemaBridgeshould suffice - striping prototype is needed only during SimpleSchema validation.