Meteor node-simple-schema allowedValues/custom functions - meteor

I'm trying to make two fields interdependent using Adeed node-simple-schema like this:
const Schemas = {};
Schemas.User= new SimpleSchema({
name: { type: String },
surname: {
type: String,
allowedValues(){
if ( this.field('name').value === "Daniel" ) {
return SomeCollection.find().fetch().map(obj => ...)
}
}
});
But it's not possible to use this.field and other function specefic to custom() inside allowedValues().
What I want: make allowedValues to depend on what the user put in the name field ( this.field('name').value === "Daniel" ). Or is it possible to use allowedValues inside custom()?
custom(){
...
return allowedValues; // limit what the user can provide to the field.
}
Note: I'm using UniForms not AutoForm.

I finally solved my problem on the client using UniForms.
This is how i got the field value:
<AutoForm
schema={ Schemas.User }
onChangeModel={ model => (
model.name ? this.setState({ name: model.name }) : ''
)}
...
>
...
</Autoform>
Using onChangeModel={ model.name ? this.setState({ name: model.name }) : '' )} I'm able to store the value of name anytime the model changes. Reference
I think one can also use findValue. But i did not tried it since onChangeModel satisfied my needs.

Related

Is there a way to show related model ids without sideloading or embedding data

My understanding is that using serializeIds: 'always' will give me this data, but it does not.
Here's what I'm expecting:
{
id="1"
title="some title"
customerId="2"
}
Instead the output I'm receiving is:
{
id="1"
title="some title"
}
My code looks something like this:
import {
Server,
Serializer,
Model,
belongsTo,
hasMany,
Factory
} from "miragejs";
import faker from "faker";
const ApplicationSerializer = Serializer.extend({
// don't want a root prop
root: false,
// true required to have root:false
embed: true,
// will always serialize the ids of all relationships for the model or collection in the response
serializeIds: "always"
});
export function makeServer() {
let server = newServer({
models: {
invoice: Model.extend({
customer: belongsTo()
}),
customer: Model.extend({
invoices: hasMany()
})
},
factories: {
invoice: Factory.extend({
title(i) {
return `Invoice ${i}`;
},
afterCreate(invoice, server) {
if (!invoice.customer) {
invoice.update({
customer: server.create("customer")
});
}
}
}),
customer: Factory.extend({
name() {
let fullName = () =>
`${faker.name.firstName()} ${faker.name.lastName()}`;
return fullName;
}
})
},
seeds(server) {
server.createList("invoice", 10);
},
serializers: {
application: ApplicationSerializer,
invoice: ApplicationSerializer.extend({
include: ["customer"]
})
},
routes() {
this.namespace = "api";
this.get("/auth");
}
});
}
Changing the config to root: true, embed: false, provides the correct output in the invoice models, but adds the root and sideloads the customer, which I don't want.
You've run into some strange behavior with how how serializeIds interacts with embed.
First, it's confusing why you need to set embed: true when you're just trying to disable the root. The reason is because embed defaults to false, so if you remove the root and try to include related resources, Mirage doesn't know where to put them. This is a confusing mix of options and Mirage should really have different "modes" that take this into account.
Second, it seems that when embed is true, Mirage basically ignores the serializeIds option, since it thinks your resources will always be embedded. (The idea here is that a foreign key is used to fetch related resources separately, but when they're embedded they always come over together.) This is also confusing and doesn't need to be the case. I've opened a tracking issue in Mirage to help address these points.
As for you today, the best way to solve this is to leave root to true and embed false, which are both the defaults, so that serializeIds works properly, and then just write your own serialize() function to remove the key for you:
const ApplicationSerializer = Serializer.extend({
// will always serialize the ids of all relationships for the model or collection in the response
serializeIds: "always",
serialize(resource, request) {
let json = Serializer.prototype.serialize.apply(this, arguments);
let root = resource.models ? this.keyForCollection(resource.modelName) : this.keyForModel(resource.modelName)
return json[root];
}
});
You should be able to test this out on both /invoices and /invoices/1.
Check out this REPL example and try making a request to each URL.
Here's the config from the example:
import {
Server,
Serializer,
Model,
belongsTo,
hasMany,
Factory,
} from "miragejs";
import faker from "faker";
const ApplicationSerializer = Serializer.extend({
// will always serialize the ids of all relationships for the model or collection in the response
serializeIds: "always",
serialize(resource, request) {
let json = Serializer.prototype.serialize.apply(this, arguments);
let root = resource.models ? this.keyForCollection(resource.modelName) : this.keyForModel(resource.modelName)
return json[root];
}
});
export default new Server({
models: {
invoice: Model.extend({
customer: belongsTo(),
}),
customer: Model.extend({
invoices: hasMany(),
}),
},
factories: {
invoice: Factory.extend({
title(i) {
return "Invoice " + i;
},
afterCreate(invoice, server) {
if (!invoice.customer) {
invoice.update({
customer: server.create("customer"),
});
}
},
}),
customer: Factory.extend({
name() {
return faker.name.firstName() + " " + faker.name.lastName();
},
}),
},
seeds(server) {
server.createList("invoice", 10);
},
serializers: {
application: ApplicationSerializer,
},
routes() {
this.resource("invoice");
},
});
Hopefully that clears things up + sorry for the confusing APIs!

How do you update a Set on DynamoDB using JavaScript document client?

I'm trying to add some items to a DynamoDB set. This worked fine with the original JavaScript SDK, but not with the new DocumentClient, using the createSet() function. Here's my code:
'use strict';
let docClient = new AWS.DynamoDB.DocumentClient({
region: 'us-east-2',
accessKeyId: 'AKIAJWIR35J4YZF4RQVQ',
secretAccessKey: 'xxxx'
});
var params = {
TableName : 'qa_Web_Application',
Key: {'Application_ID': '78f27a00-11f6-49cc-9adb-ae0795cf79d4'},
UpdateExpression : 'ADD #idList :newIds',
ExpressionAttributeNames : {
'#idList' : 'ids'
},
ExpressionAttributeValues : {
':newIds' : docClient.createSet([1,2])
}
};
console.log( params.ExpressionAttributeValues[":newIds"] );
docClient.update(params, function(err,data) {
if( err !== null ) {
console.log( err, err.stack );
} else {
console.log ( data );
}
});
Here's the output:
constructor {values: Array(2), type: "Number"}
Error: Invalid UpdateExpression: Incorrect operand type for operator or function; operator: ADD, operand type: MAP
I've seen this same question here (How do you update a Set on DynamoDB using JavaScript document client?), but that's what I'm basing this code example on, and it fails.

Meteor collections: upsert w/ array

Forms of this question have been asked a few times, but I've been unable to find a solution:
I have a schema like this (simplified):
StatusObject = new SimpleSchema({
statusArray: [statusSchema]
});
where statusSchema is
{
topicId:{
type: String,
optional: true
},
someInfo:{
type: Number,
optional: true,
decimal: true
},
otherInfo:{
type: Number,
optional: true
}
}
I am trying to upsert - with the following meteor method code:
var upsertResult = BasicInfo.update({
userId: this.userId,
statusArray: {
$elemMatch: { topicId : newStatus.topicId }
}
}, {
$set: {
"statusArray.$.topicId": newStatus.topicId,
"statusArray.$.someInfo": newStatus.someInfo,
"statusArray.$.otherInfo": newStatus.otherInfo
}
}, {
multi: true,
upsert: true
});
But I keep getting an error: statusArray must be an array
I thought by adding the $, I was making sure it is recognized as an array? What am I missing?
It seems (after your clarification comments), that you want to find a document with particular userId and modify its statusArray array using one of these scenarios:
Update existing object with particular topicId value;
Add a new object if the array doens't have one with particular topicId value.
Unfortunately, you can't make it work using just one DB query, so it should be like this:
// try to update record
const updateResult = BasicInfo.update({
userId: this.userId,
'statusArray.topicId': newStatus.topicId
}, {
$set: {
"statusArray.$": newStatus
}
});
if (!updateResult) {
// insert new record to array or create new document
BasicInfo.update({
userId: this.userId
}, {
$push: {
statusArray: newStatus
},
$setOnInsert: {
// other needed fields
}
}, {
upsert: true
});
}
Your code is treating StatusArray as an object,
Before you do the upsert, build the status array first, assuming that your current value is currentRecord
newStatusArray = currentRecord.statusArray
newStatusArray.push({
topicId: newStatus.topicId,
someInfo : newStatus.someInfo,
otherInfo: newStatus.otherInfo
})
and in the upsert, simply refer to it like this
$set: { statusArray: newStatusArray}

Populating SELECT options from database in SimpleSchema

Using SimpleSchema in Meteor with the AutoForm + Select2 plugins , i am trying to generate the Options for a Select field from the database.
The 'occupation' collection is published, and a collection 'Occupation' is defined in Meteor.
In SimpleSchema I have this:-
occupations: {
type: [String],
optional:true,
label: 'Occupation',
autoform:{
type:"select2",
placeholder: 'Comma spaced list of occupations',
options: function () {
Meteor.subscribe('occupations');
return Occupations.find({});
}
}
},
But it does not return the collection results, and crashes the application without an error message.
It seems the best way to handle this is to supply the options list through a helper.
{{> afQuickField name='occupations' multiple=true tags=true options=listOccupations}}
Where listOccupations is a helper within the template containing the form.
Template.myForm.helpers({
listOccupations: function () {
Meteor.subscribe('occupations');
return Occupations.find({}).fetch();
}
});
And we remove the options object from the schena
occupations: {
type: [String],
optional:true,
label: 'Occupation',
autoform:{
type:"select2",
placeholder: 'Comma spaced list of occupations',
}
},
Have you tried, this approach:
autoform: {
options: {
var occupations = [];
Occupations.find().map(function(occ) {
occupations.push(
{label: occ.description, value: occ._id}
);
});
return occupations;
}
}
hope this helps..
I had the same issue. I'm defining my collections schema in /lib/collections folder, and that is running on both server and client side. Given that, the console.log that I had was printing the correct values on server side and an empty array on client side.
What I did was:
if (Meteor.isClient){
Meteor.subscribe('service-categories-list', {
onReady: function(){
const serviceCategories = ServiceCategories.find({}).map(function(item, index) {
return {
label: item.name,
value: item.slug
};
});
Schema2._schema.type.autoform.options = serviceCategories;
}
})
}
I know that using the _schema is not a good idea, but I accept suggestions :)

Meteor collection2 type Object

I'm trying to create a field modifiedBy with type: Object (to Meteor users).
I see you can setup blackbox: true for a Custom Object, but if I want to setup to a specific Object say a Group (collection) field modifiedBy is the logged in user, any pointers/help is greatly appreciated.
Thanks
As far as I see it, you have two options:
Store user-ids there with type: String
Denormalize it as you proposed
Denormalize it as you proposed
To denormalize it you can do something like this inside your schema:
...
modifiedBy: {
type: object
}
'modifiedBy._id': {
type: String,
autoValue: function () {
return Meteor.userId()
}
}
'modifiedBy.username': {
type: String,
autoValue: function () {
return Meteor.user().username
}
}
...
As you pointed out, you'd want to update these properties when they change:
server-side
Meteor.users.find().observe({
changed: function (newDoc) {
var updateThese = SomeCollection.find({'modifiedBy.username': {$eq: newDoc._id}})
updateThese.forEach () {
SomeCollection.update(updateThis._id, {$set: {name: newDoc.profile.name}})
}
}
})
Store user-ids there with type: String
I'd recommend storing user-ids. It's cleaner but it doesn't perform as well as the other solution. Here's how you could do that:
...
modifiedBy: {
type: String
}
...
You could also easily write a Custom Validator for this. Now retrieving the Users is a bit more complicated. You could use a transform function to get the user objects.
SomeCollection = new Mongo.Collection('SomeCollection', {
transform: function (doc) {
doc.modifiedBy = Meteor.users.findOne(doc.modifiedBy)
return doc
}
})
But there's a catch: "Transforms are not applied for the callbacks of observeChanges or to cursors returned from publish functions."
This means that to retrieve the doc reactively you'll have to write an abstraction:
getSome = (function getSomeClosure (query) {
var allDep = new Tacker.Dependency
var allChanged = allDep.changed.bind(allDep)
SomeCollection.find(query).observe({
added: allChanged,
changed: allChanged,
removed: allChanged
})
return function getSome () {
allDep.depend()
return SomeCollection.find(query).fetch()
}
})

Resources