I'm using autoform and simple schema and I've defined a schema object with the following field:
confirm_nominee: {
type: Boolean,
autoform: {
type: "select-checkbox-inline",
options: function () {
return [
{
label: "Check here to certify that the volunteer nominated in the application is not a current member of the organization’s Board of Directors.",
value: 1
}
];
}
}
},
I just want it so if the box is left unchecked, the error reports "This field is required" and if it's checked, then the user is good to go.
This seems like it should be a really simple validation of whether the checkbox is checked or not. I've tried adding in a defaultValue of 0, but that doesn't work either.
Any thoughts?
Thanks so much.
Set in your schema rules the key allowedValues: [true] and the checkbox must be checked to be true and pass validation
confirm_nominee: {
type: Boolean,
autoform: {
type: "select-checkbox-inline",
allowedValues: [true],
options: function () {
return [
{
label: "Check here to certify that the volunteer nominated in the application is not a current member of the organization’s Board of Directors.",
value: 1
}
];
}
}
Related
I'm trying to insert an array into an object and I'm not having any luck. I think the schema is rejecting it based on validation but I'm not sure why. If I console.log(this.state.typeOfWork) and check typeof it states its an Object which contains:
(2) ["Audit - internal", "Audit - external"]
0: "Audit - internal"
1: "Audit - external"
My collection after an update contains:
"roleAndSkills": {
"typeOfWork": []
}
Example: Schema
roleAndSkills: { type: Object, optional: true },
'roleAndSkills.typeOfWork': { type: Array, optional: true },
'roleAndSkills.typeOfWork.$': { type: String, optional: true }
Example: update
ProfileCandidate.update(this.state.profileCandidateCollectionId, {
$set: {
roleAndSkills: {
typeOfWork: [this.state.typeOfWork]
}
}
});
Simple schema has some problems with validation on Objects or Arrays, i had the same problem in a recent app i developed.
What can you do?
well, what i did, on the Collections.js file, when you are saying:
typeOfWork:{
type: Array
}
Try adding the property blackbox:true, like this:
typeOfWork:{
blackbox: true,
type: Array
}
This will tell your Schema that this field is taking an Array, but ignore further validation.
The validation i made was on main.js, just to be sure i had no empty array and the data was plain text.
As requested here is my update method, im my case i used objects not arrays but it works the same way.
editUser: function (editedUserVars, uid) {
console.log(uid);
return Utilizadores.update(
{_id: uid},
{$set:{
username: editedUserVars.username,
usernim: editedUserVars.usernim,
userrank: {short: editedUserVars.userrank.short,
long: editedUserVars.userrank.long},
userspec: {short: editedUserVars.userspec.short,
long: editedUserVars.userspec.long},
usertype: editedUserVars.usertype}},
{upsert: true})
},
here it the collection schema
UtilizadoresSchema = new SimpleSchema({
username:{
type: String
},
usernim:{
type: String
},
userrank:{
blackbox: true,
type: Object
},
userspec:{
blackbox: true,
type: Object
},
usertype:{
type: String
}
});
Utilizadores.attachSchema(UtilizadoresSchema);
Hope it helps
Rob
typeOfWork is an Array. You should push your value in it :
$push: {
"roleAndSkills.typeOfWork": this.state.typeOfWork
}
for multiple values :
$push: {
"roleAndSkills.typeOfWork": { $each: [ "val1", "val2" ] }
}
mongo $push operator
mongo dot notation
You state that this.state.typeOfWork is an array (of strings) but then when you .update() your document you are enclosing it in square brackets:
ProfileCandidate.update(this.state.profileCandidateCollectionId, {
$set: {
roleAndSkills: {
typeOfWork: [this.state.typeOfWork]
}
}
});
Simply remove the redundant square brackets:
ProfileCandidate.update(this.state.profileCandidateCollectionId, {
$set: {
roleAndSkills: {
typeOfWork: this.state.typeOfWork
}
}
});
Also since your array is just an array of strings you can simplify your schema a bit by declaring it as such with [String] for the type:
'roleAndSkills.typeOfWork': { type: [String] }
Note furthermore that objects and arrays are by default optional so you can even omit the optional flag.
I'm having an issue setting up roles in my project that uses meteor-collection2. I assume this is the roles package noted in the collection2 docs.
I'm using accounts-password and ian:accounts-ui-bootstrap-3 as my accounts solution. Here's my config for it:
Accounts.ui.config({
requestPermissions: {},
extraSignupFields: [{
fieldName: 'first-name',
fieldLabel: 'First name',
inputType: 'text',
visible: true,
validate: function(value, errorFunction) {
if (!value) {
errorFunction("Please write your first name");
return false;
} else {
return true;
}
}
}, {
fieldName: 'last-name',
fieldLabel: 'Last name',
inputType: 'text',
visible: true,
}, {
fieldName: 'terms',
fieldLabel: 'I accept the terms and conditions',
inputType: 'checkbox',
visible: true,
saveToProfile: false,
validate: function(value, errorFunction) {
if (value) {
return true;
} else {
errorFunction('You must accept the terms and conditions.');
return false;
}
}
}]
});
I added the roles field to my Users Schema:
Schemas.User = new SimpleSchema({
username: {
type: String,
// For accounts-password, either emails or username is required, but not both. It is OK to make this
// optional here because the accounts-password package does its own validation.
// Third-party login packages may not require either. Adjust this schema as necessary for your usage.
optional: true
},
emails: {
type: [Object],
optional: true
},
"emails.$.address": {
type: String,
regEx: SimpleSchema.RegEx.Email
},
"emails.$.verified": {
type: Boolean
},
createdAt: {
type: Date
},
services: {
type: Object,
optional: true,
blackbox: true
},
profile: {
type: Object,
optional: true,
blackbox: true
},
"first-name": {
type: String
},
"last-name": {
type: String
},
// Add `roles` to your schema if you use the meteor-roles package.
// Option 1: Object type
// If you specify that type as Object, you must also specify the
// `Roles.GLOBAL_GROUP` group whenever you add a user to a role.
// Example:
// Roles.addUsersToRoles(userId, ["admin"], Roles.GLOBAL_GROUP);
// You can't mix and match adding with and without a group since
// you will fail validation in some cases.
roles: {
type: Object,
optional: true,
blackbox: true
}
});
And now I want to immediately create one user on the first time I run my project with an admin role and stop any others from being created afterwards:
/*----------------------------------------------- #2 Create admin user ----------------------------------------------*/
/*Notes: Create an admin-type user if no users exist yet.*/
if (Meteor.users.find().count() === 0) { /*------------------------------------ If there are no users created yet*/
var users = [{
username: "admin",
name: "admin",
email: "test#test.com",
roles: ['admin']
}];
_.each(users, function(user) {
var id = Accounts.createUser({
username: user.username,
email: user.email,
password: "mypassword123",
profile: {
name: user.name
},
first-name: Me,
last-name: MeName
});
if (user.roles.length > 0) {
// Need _id of existing user record so this call must come
// after `Accounts.createUser` or `Accounts.onCreate`
Roles.addUsersToRoles(id, user.roles);
}
});
}
/*-------------------------------------------------------------------------------------------------------------------*/
/*Prevent non-authorized users from creating new users*/
Accounts.validateNewUser(function(user) {
var loggedInUser = Meteor.user();
if (Roles.userIsInRole(loggedInUser, ['admin'])) {
return true;
}
throw new Meteor.Error(403, "Not authorized to create new users");
});
So far apparently so good: I get the new user.
The problem is when I use spacebars to hide admin features in html the created user isn't recognized as an admin and they are hidden from me...
{{#if isInRole 'admin'}}
<p>Exclusive to admin stuff</p>
{{/if}}
If using Roles as an Object (option #1) you must specify a group and permission for all users (I believe with Roles 2.0 which is coming out soon this will no longer be the case), so for something like an admin user you could use Roles.GLOBAL_GROUP which is used to apply blanket permissions across all groups.
For this, you would need to make the follow change:
Roles.addUsersToRoles(id, user.roles);
To this:
Roles.addUsersToRoles(id, user.roles, Roles.GLOBAL_GROUP);
You will also need to specify the group inside of your isInRole helper, here's an example of how that would look:
Roles.addUsersToRoles(joesUserId, ['manage-team'], 'manchester-united.com')
//manchester-united.com is the group
For your isInRole helper on the client, you would use this:
{{#if isInRole 'manage-team' 'manchester-united.com'}}
<h3>Manage Team things go here!</h3>
{{/if}}
You are currently using it as a String (Option #2, without groups). If you are planning on using groups for any users then you will need to make the changes I explained above (you can then remove option #2 as well), but if you don't plan on using groups for any users then you can remove Option #1 and simply use it as a String.
There is a helpful tutorial on the Roles package here, and the package docs are great too.
I have a schema like so (fluff cut out):
Schemas.people = new SimpleSchema({
note: {
type: [Schemas.notes],
optional: true,
defaultValue: []
},
updates: {
type: [Schemas.updates],
optional:true,
autoValue:function(){
if (this.isInsert) {
return [{
at: new Date,
user_id: this.userId
}];
}
return {
$push:{
at: new Date,
user_id: this.userId
}
}
}
}
});
And the notes schema looks like:
Schemas.notes = new SimpleSchema({
note: {
type: String,
autoform: {
afFieldInput:{
type:"textarea"
}
},
optional: true
},
updates: {
type: [Schemas.updates],
optional:true,
autoform:{
omit:true
},
autoValue:function(){
if (this.isInsert) {
return [{
at: new Date,
user_id: this.userId
}];
}
return {
$push:{
at: new Date,
user_id: this.userId
}
}
}
}
});
And the updates schema is super simple:
Schemas.updates = new SimpleSchema({
at: {
type: Date
},
user_id:{
type: Meteor.ObjectID
}
});
The "updates" field on the people schema saves the date/user id as expected when an update is made. However, it fails on the notes schema:
SimpleSchema invalid keys for "blablabla" context:
0: Object
name: "note.0.updates.0.at"
type: "keyNotInSchema"
value: Mon May 11 2015 11:57:58 GMT-0400 (Eastern Daylight Time)
1: Object
name: "note.0.updates.0.user_id"
type: "keyNotInSchema"
value: "abcd1234"
I believe that the "name" should look like "people.note.0.updates.0.at" but I'm unsure that this assumption is correct and I'm completely unsure how to go about making that happen.
Update:
Code used to update people
{{#autoForm collection="people" id=formId type="update" class="update" autocomplete="off" doc=getDocument autosave=true template="quickform"}}
{{> afQuickField name='note' template="quickform" }}
{{/autoForm}}
formId returns a randomish ID string and getDocument passes in the correct collection.
Schemas.notes._schemaKeys does not list the at and user_id fields... but Schemas.people._schemaKeys does.
People schema shows: [..., "updates.$.at", "updates.$.user_id", ...]
Notes schema shows: ["note", "updates", "updates.$"]
How bizarre.
Note that Meteor uses standard JavaScript syntax and therefore has the same restrictions, for example as you already realized order of code is important.
Let's have a look.
Schemas.notes = new SimpleSchema({
updates: {
type: [Schemas.updates]
}
}
There are no nested functions in this code, therefore every code of line will be executed, before Meteor continues with the next Schema definition. Schema.updates will be dereferenced immediately, although it isn't set yet. type will be an array containing null and that finally makes SimpleSchema assume that no fields are allowed at all.
The issue is with the order in which the schemas are declared. Which I suppose makes sense? I was declaring "notes" before "updates", and "people" last. Putting "updates" first fixed the issue completely.
I'm going to report this as a possible bug to the collection repo.
I want to be able to select several users from a list of users.
I am user collection2, simple-schema and autoform.
I'd like to generate a simple quickForm for doing this. Here's my simple-schema:
Schemas.Item = new SimpleSchema({
name: {
type: String,
label: "Name",
max: 100
},
userIds: {
type: [String],
regEx: SimpleSchema.RegEx.Id
}
});
Looking at the autoform docs, I noticed that I want to have a select view so I need to pass in options.
I'd like to be able to do this right in my schema!
userIds: {
type: [String],
regEx: SimpleSchema.RegEx.Id
options: function() {
// return users with {value:_id, label:username}
}
}
Otherwise, I'd have to generate a template with quickFormFields just to pass in the options.
Just to pile things on, there shouldn't be any duplicate userIds...
Thanks for any help
Probably you have already found an answer, but maybe somebody will find it useful. I have lots of different things to specify once you select user, that's why my type for users is [Object]. In your case, you can modify that. The most important part is autoform.options method and seems to be the part you were looking for.
users: {
type: [Object]
},
"users.$.id": {
// you can use your own type, e.g. SimpleSchema.RegEx.Id, as I am using custom Schema for accounts
type: Schemas.Account._id,
label: 'Select user',
autoform: {
options: function () {
var options = [];
Meteor.users.find().forEach(function (element) {
options.push({
label: element.username, value: element._id
})
});
return options;
}
}
}
Snippet above will give you the list of all the users so you can easily select them from dropdown list.
Remember to add appropriate publish method to make that working as without that you will always get only currently logged one.
We are using a server based simple grid. The grid reads and updates data for a remote data source. It has two columns that are using drop down editors. Everything seems to work fine except that after saving, when editor closes, the changed values are not displayed in the edited row. It still shows the old value. When we try to refresh the grid using the sync event, it changes the order of the rows however, it does update the values on refresh.
It seems like the template function is not executed after the update is completed. How to edit the grid / code to ensure that the changed value is reflected in the grid?
Grid Definition code:
$("#servicetype-grid").kendoGrid({
pageable: true,
toolbar: [{name: "create", text: ""}, { template: kendo.template($("#servicetype-search-template").html())}],
columns: [
{
field: "serviceName", title: "Service Name"
},
{
field: "billCalculationTypeId",
editor: calculationTypeDropDownEditor,
template: function(dataItem) {
return kendo.htmlEncode(dataItem.billCalculationTypeName);
},
title: "Bill Calculation Type"
},
{
field: "payCalculationTypeId",
editor: calculationTypeDropDownEditor,
template: function(dataItem) {
return kendo.htmlEncode(dataItem.payCalculationTypeName);
},
title: "Pay Calculation Type"
},
{
command: [
{ name: "edit", text: { edit: "", cancel: "", update: "" }},
{ name: "destroy", text:""}
],
title: "Actions"
}
],
dataSource: dataSource,
sortable: {
mode: "single",
allowUnsort: false
},
dataBound: function(e) {
setToolTip();
},
edit: function(e) {
$('.k-grid-update').kendoTooltip({content: "Update"});
$('.k-grid-cancel').kendoTooltip({content: "Cancel"});
},
cancel: function(e) {
setToolTip();
},
editable: {
mode: "inline",
confirmation: "Are you sure that you want to delete this record?"
}
});
Drop down function is defined as:
function calculationTypeDropDownEditor(container, options) {
$('<input required data-text-field="name" data-value-field="id" data-bind="value:' + options.field + '"/>')
.appendTo(container)
.kendoDropDownList({
autoBind: false,
dataSource: {
transport: {
read: {
dataType: "jsonp",
url: baseURL + "rips/services/calculationType/lookupList"
}
}
}
});
}
After doing some search on Google and browsing through different examples on Kendo site, I finally was able to resolve this issue. Following is my understanding of the problem and solution:
When we are using drop down box or combo box as a custom editor, generally we have a different datasource that contains a list options with an id and a value to show. I defined the template as "#=field.property#" of the field that I was looking up. In my case it was calculation type. Following is my model:
model: {
id: "serviceId",
fields: {
serviceName: { field:"serviceName", type: "string", validation: { required: { message: "Service Name is required"}} },
billCalculationType: { field: "billCalculationType", validation: { required: true}},
payCalculationType: { field: "payCalculationType", validation: { required: true} }
}
In above, billCalculationType and payCalculationType are supposed to be drop down list values displaying the calculation type name but storing the id of the corresponding calculation type. So, in my grid definition, I added following:
{ field: "billCalculationType",
editor: calculationTypeDropDownEditor,
template:"#=billCalculationType.name#",
title: "Bill Calculation Type" },
Where calculation dropdown editor is a function that builds a drop down from external data source. So, it works fine. However, for the template definition to work in (field.property) format, the server must return the value as a class for this field and not a simple text. So, in my server service, I returned in following format:
{"billCalculationType":{"id":"4028828542b66b3a0142b66b3b780001","name":"Hourly","requiredDetails":false},"payCalculationType":{"id":"4028828542b66b3a0142b66b3b960002","name":"Kilometers","requiredDetails":false},"serviceId":"4028828542b66b3a0142b66b3c16000a","serviceName":"XYZ"}
Notice that the billCalculationType and payCalculationType are returned as objects with name and id as properties. This allows the template to work properly.
Hope this helps.