How to create reusable component with simpleSchema - meteor

I might be thinking about this the wrong way, so please feel free to correct my thinking.
I'm using simpleSchema and I have a section of code which is used in more than one schema. Is there a way to create an individual component and import it into each schema, so that when I need to update the component I don't have to update it in multiple locations?
Path: resuableComponent
type: String,
optional: true,
autoform: {
type: "select",
options: function () {
return [
{label: "School logo 1", value: 'url'},
{label: "School logo 2", value: 'url'},
{label: "School logo 3", value: 'url'},
];
},
}
Path: studentCollection.js
Schemas.Student = new SimpleSchema({
studentUserId: {
type: String,
},
school: {
type: String,
optional: false
},
**resuableComponent**
});
Path: teacherCollection.js
Schemas.Teacher = new SimpleSchema({
teacherUserId: {
type: String,
},
school: {
type: String,
optional: false
},
**resuableComponent**
});

You could move the reusable objects into a different file that should be visible on both client and server if you are using SimpleSchema.
Example based on your question:
lib/schema-components.js :
SchemaComponents = {
school: {
type: String,
optional: false
},
// ...
// more reusable components here
};
someCollectionFile.js :
Schemas.Student = new SimpleSchema({
studentUserId: {
type: String
},
school: SchemaComponents.school,
// ...
});

Related

Saving API call result to a collection but recieving errors while setting up a SimpleSchema

I have a Meteor method which does an API call, then the response of the call is saved to the users collection. I'm using the Collection2 package with my project and I'm a little bit lost setting up my SimpleSchema for this.
Here is what the JSON response looks like from the API call:
[{"keyword":"2i","url":"http://example.com","title":"Example","timestamp":"2016-11-05
08:54:42","ip":"00.00.00.000","clicks":"2","user":"HweoSCY2ujscjJ9Zl"},{"keyword":"2j","url":"http://example.com","title":"YouTube","timestamp":"2016-11-06
02:11:18","ip":"00.00.00.000","clicks":"1","user":"HweoSCY2ujscjJ9Zl"},{"keyword":"2k","url":"http://example.com","title":"YouTube","timestamp":"2016-11-08
03:35:12","ip":"00.00.00.000","clicks":"0","user":"HweoSCY2ujscjJ9Zl"}]
Here's currently how I've been able to save this data to the users collection:
Meteor.users.update(Meteor.userId(), {
$set: { 'shortURLs.URLS': result.data }
});
This works and looks like this in the db:
My issue is that I'd like to have a SimpleSchema setup for this so that the "timestamp" will be saved as a Date instead of a String, but everytime I try and create a schema for it I just receive errors like "After filtering out keys not in the schema, your modifier is now empty". I have tried a lot of different variations to try and make it work but none of them have been successful, here's just currently where it's at:
Schema.ShortURLs = new SimpleSchema({
shortURLs: {
type: Object
},
'shortURLs.$': {
type: Object
},
'shortURLs.$.keyword': {
type: String,
optional: true,
label: "Keyword"
},
'shortURLs.$.url': {
type: String,
optional: true,
label: "URL"
},
'shortURLs.$.title': {
type: String,
optional: true,
label: "Title"
},
'shortURLs.$.timestamp': {
type: Date,
optional: true,
label: "Timestamp"
},
'shortURLs.$.ip': {
type: String,
optional: true,
label: "IP"
},
'shortURLs.$.clicks': {
type: String,
optional: true,
label: "Clicks"
},
'shortURLs.$.user': {
type: String,
optional: true,
label: "User"
},
});
This is then attached apart of a User Simple Schema:
...
shortURLs: {
type: Schema.ShortURLs,
optional: true
},
...
And I have that attached to the users collection:
Meteor.users.attachSchema(Schema.User);
I don't think there's an issue with how I have it attached as I have other SimpleSchemas setup the same way and they're working fine, I believe the issue is how I have this particular one written. Any help here would be extremely appreciated.
You need to define the shortURLs type in the Meteor.users collection as type: [Schema.ShortURLs], i.e. a list of type Schema.ShortURLs
Schema = {}
Schema.ShortURLs = new SimpleSchema({
keyword: {
type: String,
optional: true,
label: "Keyword"
},
url: {
type: String,
optional: true,
label: "URL"
},
title: {
type: String,
optional: true,
label: "Title"
},
timestamp: {
type: Date,
optional: true,
label: "Timestamp"
},
ip: {
type: String,
optional: true,
label: "IP"
},
clicks: {
type: String,
optional: true,
label: "Clicks"
},
user: {
type: String,
optional: true,
label: "User"
},
});
Schema.User = new SimpleSchema({
...
shortURLs: {
type: [Schema.ShortURLs],
optional: true
},
...
});
...
Meteor.users.attachSchema(Schema.User);

Schema error when reusing same field

I have two separate schemas that use the same field. I've tried to create schemaComponents so that I can update both schemas in one location however I get an error Error: Invalid definition for school.$ field. when I use it. I'm not sure what I'm doing wrong here, I was under the impression this was allowed.
Path: SchemaComponents.js
SchemaComponents = {
schools: {
type: [String],
optional: true,
autoform: {
options: [
{label: "School One", value: 'SchoolOne'},
{label: "School Two", value: 'SchoolTwo'},
{label: "School Three", value: 'SchoolThree'},
]
}
}
};
Path: StudentSchema.js
import from '../components/SchemaComponents.js';
StudentSchema = new Mongo.Collection("studentSchema");
var Schemas = {};
Schemas.StudentSchema = new SimpleSchema({
school: SchemaComponents.schools,
});
StudentSchema.attachSchema(Schemas.StudentSchema);
Path: TeacherSchema.js
import from '../components/SchemaComponents.js';
TeacherSchema = new Mongo.Collection("teacherSchema");
var Schemas = {};
Schemas.TeacherSchema = new SimpleSchema({
school: SchemaComponents.schools,
});
TeacherSchema.attachSchema(Schemas.TeacherSchema);
You defined SchemaComponent as a simple object and not as a SimpleSchema object. To reuse your schools definition do:
let schoolsSchema = new SimpleSchema({
schools: {
type: [String],
optional: true,
autoform: {
options: [
{label: "School One", value: 'SchoolOne'},
{label: "School Two", value: 'SchoolTwo'},
{label: "School Three", value: 'SchoolThree'},
]
}
}
});
Then you can do:
Schemas.TeacherSchema = new SimpleSchema({
school: {
type: schoolsSchema
}
});

Error when referencing schemaComponents

I have two separate schemas that use the same components. I've tried to create schemaComponents so that I can update both schemas in one location however I get an error ReferenceError: SchemaComponents is not defined when I use it. I'm not sure what I'm doing wrong here, I was under the impression this was allowed.
Path: SchemaComponents.js
SchemaComponents = {
schools: {
type: [String],
optional: true,
autoform: {
options: [
{label: "School One", value: 'SchoolOne'},
{label: "School Two", value: 'SchoolTwo'},
{label: "School Three", value: 'SchoolThree'},
]
}
}
};
Path: StudentSchema.js
StudentSchema = new Mongo.Collection("studentSchema");
var Schemas = {};
Schemas.StudentSchema = new SimpleSchema({
school: SchemaComponents.schools,
});
StudentSchema.attachSchema(Schemas.StudentSchema);
Path: TeacherSchema.js
TeacherSchema = new Mongo.Collection("teacherSchema");
var Schemas = {};
Schemas.TeacherSchema = new SimpleSchema({
school: SchemaComponents.schools,
});
TeacherSchema.attachSchema(Schemas.TeacherSchema);

Meteor collection2 not updating

I'm having some trouble with updating a user account. I use the following schema (collection2):
lib/collections/users.js
Users = Meteor.users;
var Schemas = {};
Schemas.User = new SimpleSchema({
gender: {
type: Number,
min: 1
},
s_gender: {
type: Number,
min: 1,
optional:false
},
picture: {
type: String,
custom: function() {
var base64Matcher = new RegExp("^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{4})$");
var value = this.value.replace("data:image/png;base64,","");
if(!base64Matcher.test(value))
{
return 'no picture';
}
else
{
return true;
}
}
}
});
Users.attachSchema(Schemas.User);
Now I do the update with the following code:
client/templates/start.js
Users.update({_id: Meteor.userId()}, {
$set: {picture: picture, gender: gender, s_gender: s_gender}
}, {validationContext: "updateUser"}, function (error, result) {
if (error) {
errorObjs = Users.simpleSchema().namedContext("updateUser").invalidKeys();
console.log(errorObjs);
}
console.log(result);
});
The validation passes, but I only get a "0" in results (errors are null) - the update isn't working. Errors are shown if I have an empty field, so the validation is working well. If I detach the schema, the update works fine.
Did I forget something here or why isn't he updating when validation passes?
// Edit: Also I see, that Meteor doesn't create users anymore.
I believe you need to use Users.profile.foo instead of Users.foo, because Users is a special meteor collection and you can only save new fields inside the profile field. Try using Simple Schema/Collection 2 suggested User schema as a starting point (I'll copy it bellow). Notice the "profile schema" is loaded before the user schema:
Schema = {};
Schema.UserProfile = new SimpleSchema({
firstName: {
type: String,
regEx: /^[a-zA-Z-]{2,25}$/,
optional: true
},
lastName: {
type: String,
regEx: /^[a-zA-Z]{2,25}$/,
optional: true
},
birthday: {
type: Date,
optional: true
},
gender: {
type: String,
allowedValues: ['Male', 'Female'],
optional: true
},
organization : {
type: String,
regEx: /^[a-z0-9A-z .]{3,30}$/,
optional: true
},
website: {
type: String,
regEx: SimpleSchema.RegEx.Url,
optional: true
},
bio: {
type: String,
optional: true
}
});
Schema.User = new SimpleSchema({
username: {
type: String,
regEx: /^[a-z0-9A-Z_]{3,15}$/
},
emails: {
type: [Object],
optional: true
},
"emails.$.address": {
type: String,
regEx: SimpleSchema.RegEx.Email
},
"emails.$.verified": {
type: Boolean
},
createdAt: {
type: Date
},
profile: {
type: Schema.UserProfile,
optional: true
},
services: {
type: Object,
optional: true,
blackbox: true
}
});
Meteor.users.attachSchema(Schema.User);
source: https://github.com/aldeed/meteor-collection2

How should I insert into Meteor collection using autoform/collection2?

I'm trying to do the autoform books example using Meteor. How exactly should I do the Books.insert ?
I see the example:
Books.insert({title: "Ulysses", author: "James Joyce"}, function(error, result) {
//The insert will fail, error will be set,
//and result will be undefined or false because "copies" is required.
//
//The list of errors is available on
//`error.invalidKeys` or by calling
Books.simpleSchema().namedContext().invalidKeys()
});
I'm not entirely sure how I should hook this up with the rest of my code:
if (Meteor.isClient) {
Books = new Meteor.Collection("books");
var Schemas = {};
Schemas.Book = new SimpleSchema({
title: {
type: String,
label: "Title",
max: 200,
optional: true
},
author: {
type: String,
label: "Author",
optional: true
},
copies: {
type: Number,
label: "Number of copies",
min: 0,
optional: true
},
lastCheckedOut: {
type: Date,
label: "Last date this book was checked out",
optional: true
},
summary: {
type: String,
label: "Brief summary",
optional: true,
max: 1000
}
});
Books.attachSchema(Schemas.Book);
}
Can anyone give me any advice on this?
I'm thinking that I would need something like this:
Template.bookform.events({
'click btn.submit': function () {
var form = document.getElementById("formID").value;
Books.insert(form);
}
});
Thanks in advance! :)
I have never used autoform but in the documentation it says that it already gives you "automatic insert and update events, and automatic reactive validation".
So there should be no need to specify your own event handler.
In the docs you will also find the books example. I am just copying from there:
JS
Books = new Meteor.Collection("books", {
schema: {
title: {
type: String,
label: "Title",
max: 200
},
author: {
type: String,
label: "Author"
},
copies: {
type: Number,
label: "Number of copies",
min: 0
},
lastCheckedOut: {
type: Date,
label: "Last date this book was checked out",
optional: true
},
summary: {
type: String,
label: "Brief summary",
optional: true,
max: 1000
}
}
});
if (Meteor.isClient) {
Meteor.subscribe("books");
}
if (Meteor.isServer) {
Meteor.publish("books", function () {
return Books.find();
});
}
HTML
<head>
<title>Book example</title>
</head>
<body>
{{> insertBookForm}}
</body>
<template name="insertBookForm">
{{> quickForm collection="Books" id="insertBookForm" type="insert"}}
</template>

Resources