Custom Validate an object Push to an array - arrays

I am using something similar to the following schema.
By visiting the Item page I can add related items to the Item's Related Items array field.
I would like to custom validate the object I am pushing to the Item's Related Items field, to test if a similar object exists in the array already - so that I do not get a duplicate.
In my code below, the custom validation does not fire. I expect this may be because custom validation cannot be applied to a type: [object], and should be applied to the properties of the object - but then I am unable to test the object as a whole.
const ItemsSchema = new SimpleSchema({
name: {
type: String,
label: 'Name',
},
related: {
type: [Object],
label: 'Related Items',
optional:true,
custom: function () {
let queryData = { docId: this.docId, related: this.value }
if (Meteor.isClient && this.isSet) {
Meteor.call("relatedObjectIsUniqueForThisItem", queryData,
function (error, result) {
if(!result){
console.log("not unique");
return "Invalid";
}
else{
return true;
}
});
}
}
},
'related.$.name':{
type: String,
label:'Name',
},
'related.$.code':{
type:String,
label:'Code',
min:5,
},
});

I figured out the way to handle this.
The custom validation should not be on the [object], but rather one of the properties of the object - in this case 'source' or 'code'.
Inside one of the object properties you can call this.siblingField(otherField); But it means you have to rebuild the object.
In my case :-
const ItemsSchema = new SimpleSchema({
name: {
type: String,
label: 'Name',
},
related: {
type: [Object],
label: 'Related Items',
optional:true,
},
'related.$.name':{
type: String,
label:'Name',
custom: function () {
//---------------------------
//this is the important bit
//---------------------------
let queryData = {
docId: this.docId,
related: {
name:this.value,
code:this.siblingField('code').value,
}
}
//---------------------------
//end of important bit
//---------------------------
if (Meteor.isClient && this.isSet) {
Meteor.call("relatedObjectIsUniqueForThisItem", queryData,
function (error, result) {
if(!result){
console.log("not unique");
return "Invalid";
}
else{
return true;
}
});
}
}
},
'related.$.code':{
type:String,
label:'Code',
min:5,
},
});

Related

update one element of array inside object and return immutable state - redux [duplicate]

In React's this.state I have a property called formErrors containing the following dynamic array of objects.
[
{fieldName: 'title', valid: false},
{fieldName: 'description', valid: true},
{fieldName: 'cityId', valid: false},
{fieldName: 'hostDescription', valid: false},
]
Let's say I would need to update state's object having the fieldName cityId to the valid value of true.
What's the easiest or most common way to solve this?
I'm OK to use any of the libraries immutability-helper, immutable-js etc or ES6. I've tried and googled this for over 4 hours, and still cannot wrap my head around it. Would be extremely grateful for some help.
You can use map to iterate the data and check for the fieldName, if fieldName is cityId then you need to change the value and return a new object otherwise just return the same object.
Write it like this:
var data = [
{fieldName: 'title', valid: false},
{fieldName: 'description', valid: true},
{fieldName: 'cityId', valid: false},
{fieldName: 'hostDescription', valid: false},
]
var newData = data.map(el => {
if(el.fieldName == 'cityId')
return Object.assign({}, el, {valid:true})
return el
});
this.setState({ data: newData });
Here is a sample example - ES6
The left is the code, and the right is the output
Here is the code below
const data = [
{ fieldName: 'title', valid: false },
{ fieldName: 'description', valid: true },
{ fieldName: 'cityId', valid: false }, // old data
{ fieldName: 'hostDescription', valid: false },
]
const newData = data.map(obj => {
if(obj.fieldName === 'cityId') // check if fieldName equals to cityId
return {
...obj,
valid: true,
description: 'You can also add more values here' // Example of data extra fields
}
return obj
});
const result = { data: newData };
console.log(result);
this.setState({ data: newData });
Hope this helps,
Happy Coding!
How about immutability-helper? Works very well. You're looking for the $merge command I think.
#FellowStranger: I have one (and only one) section of my redux state that is an array of objects. I use the index in the reducer to update the correct entry:
case EMIT_DATA_TYPE_SELECT_CHANGE:
return state.map( (sigmap, index) => {
if ( index !== action.payload.index ) {
return sigmap;
} else {
return update(sigmap, {$merge: {
data_type: action.payload.value
}})
}
})
Frankly, this is kind of greasy, and I intend to change that part of my state object, but it does work... It doesn't sound like you're using redux but the tactic should be similar.
Instead of storing your values in an array, I strongly suggest using an object instead so you can easily specify which element you want to update. In the example below the key is the fieldName but it can be any unique identifier:
var fields = {
title: {
valid: false
},
description: {
valid: true
}
}
then you can use immutability-helper's update function:
var newFields = update(fields, {title: {valid: {$set: true}}})

How can I access all elements with a particular attribute in graphQL?

I have some json data in file called countryData.json structured as so:
{
"info":"success",
"stats":
[{
"id":"1",
"name":"USA",
"type":"WEST"
},
//...
I'm using graphQL to access this data. I have created an object type in the schema for countries using the following:
const CountryType = new GraphQLObjectType({
name: "Country",
fields: () => ({
id: { type: GraphQLID },
name: { type: GraphQLString },
type: { type: GraphQLString },
})
});
I want to write a query that will allow me to access all of the elements of this array that have a certain "name" value(There can be multiple with the same name). I've written the following query, but it only returns the first match in the array:
const RootQuery = new GraphQLObjectType({
name:"RootQueryType",
fields:{
country: {
type: CountryType,
args: { type: { name: GraphQLString } },
resolve(parent, args){
return _.find(countryData.stats, {name: args.name});
}
}
}
});
The "_" comes from const _ = require('lodash');
Also, how can I just get every single item in the array?
I have not recreated the code, therefore I can not check if it would be executed correctly. This is code, that should work in my opinion (without trying). If you want to return array of elements you need to implement https://lodash.com/docs/#filter. Filter will return all objects from stats, which match the argument name. This will return correctly inside resolver function, however, your schema needs adjustments to be able to return array of countries.
You need probably rewrite the arguments as follows as this is probably not correct. You can check out how queries or mutation arguments can be defined https://github.com/atherosai/express-graphql-demo/blob/feature/2-json-as-an-argument-for-graphql-mutations-and-queries/server/graphql/users/userMutations.js. I would rewrite it as follows to have argument "name"
args: { name: { type: GraphQLString } }
You need to add GraphQLList modifier, which defines, that you want to return array of CountryTypes from this query. The correct code should look something like this
const RootQuery = new GraphQLObjectType({
name:"RootQueryType",
fields:{
country: {
type: CountryType,
args: { name: { type: GraphQLString } },
resolve(parent, args){
return _.find(countryData.stats, {name: args.name});
}
},
countries: {
type: new GraphQLList(CountryType),
args: { name: { type: GraphQLString } },
resolve(parent, args){
return _.filter(countryData.stats, {name: args.name});
}
}
}
});
Now if you call query countries, you should be able to retrieve what you are expecting. I hope that it helps. If you need some further explanation, I made the article on implementing lists/arrays in GraphQL schema as I saw that many people struggle with similar issues. You can check it out here https://graphqlmastery.com/blog/graphql-list-how-to-use-arrays-in-graphql-schema
Edit: As for the question "how to retrieve every object". You can modify the code in resolver function in a way, that if the name argument is not specified you would not filter countries at all. This way you can have both cases in single query "countries".

Adding property to nested objects in Flow causes errors

I can add a property to a flat object in Flow without any errors.
View demo
But if I add a property to a nested object using the same pattern, Flow throws an error:
/* #flow */
type Content = {
fields: {
slug: string,
},
frontmatter: {
title: string,
}
};
type CustomContent = {
fields: {
slug: string,
},
frontmatter: {
title: string,
// This is the new property:
headerImage: string,
}
};
function doSomethingWithContent(content: Content) {
return [
content.fields.slug,
content.frontmatter.title,
];
}
function doSomethingWithCustomContent(content: CustomContent) {
return [
content.fields.slug,
content.frontmatter.title,
content.frontmatter.headerImage,
];
}
Error:
doSomethingWithContent(customContent);
^ Cannot call `doSomethingWithContent` with `customContent` bound to `content` because property `headerImage` is missing in object type [1] but exists in object type [2] in property `frontmatter`.
View demo
For this to work, the frontmatter property needs to be marked as covariant, e.g.
type Content = {
fields: {
slug: string,
},
frontmatter: {
title: string,
}
};
to
type Content = {
fields: {
slug: string,
},
+frontmatter: { // Note the "+" prefix here
title: string,
}
};
If Flow didn't error on this, it would be perfectly valid for your function to do
function doSomethingWithContent(content: Content) {
content.frontmatter = { title: "" };
which make the type of content still a valid Content, but an invalid CustomContent object. By marking the property covariant, it essentially makes the frontmatter property read-only.

How can I Add and Delete nested Object in array in Angularjs

heres my output Image html How can I delete Object in array and push when adding some Data
angular.module('myApp.Tree_Service', [])
.factory('TreeService', function() {
var svc = {};
var treeDirectories = [
{
name: 'Project1',
id: "1",
type: 'folder',
collapse: true,
children: [
{
name: 'CSS',
id: "1-1",
type: 'folder',
collapse: false,
children: [
{
name: 'style1.css',
id: "1-1-1",
type: 'file'
},
{
name: 'style2.css',
id: "1-1-2",
type: 'file'
}
]
}
]
}
];
svc.add = function () {}
svc.delete = function (item, index) { }
svc.getItem = function () { return treeDirectories; }
return svc;
});
})();
I'm Newbee in Angularjs and I don't know how much to play it.
Hopefully someone can help me. Im Stucked.
Well you can delete any object by just usingdelete Objname.property
So for example you want to delete Children in treeDirectories first index object you can use delete treeDirectories[0].children if you want to delete children inside children then delete treeDirectories[0].children[0].children
if you want to remove an index from an array in lowest level children then
treeDirectories[0].children[0].children.splice(index,1)
for pushing data is for object you can directly assign value to the property you want
treeDirectories[0].children[0].newproperty = "check"
And for array you can
treeDirectories[0].children[0].children.push(object)

mongoose update with push operations on array and set operation on object

I have this mongoose schema
var ContactSchema = module.exports = new mongoose.Schema({
name: {
type: String,
required: true
},
phone: {
type: Number,
required: true,
},
messages: [
{
title: {type: String, required: true},
msg: {type: String, required: true}
}],
address:{ city:String,
state:String
}
});
I have initially the collection set with name and phone field. I need to update the collection with new messages into messages array and new address into address object. the function must also need to handle any single operation, ie in some case i have only update to messages array or updates to both name and address. so how i can i do all operations in a single function.
var messages= {
title: req.body.title,
msg: req.body.msg
}
Model.findOneAndUpdate({'_id': req.body.id,},{$push: {messages:message}},{upsert: true}, function (err, data) {
if (err) {
return res.status(500).send(err);
}
if (!data) {
return res.status(404).end();
}
return res.status(200).send(data);
});
You could try use both the $set and $push operators in your update object. Suppose, for example, you want to update both name and address fields in one single operation, use the $set on the name field and a $push operation to the address array:
var messages= {
title: req.body.title,
msg: req.body.msg
},
query = {'_id': req.body.id},
update = {
$set: {name: req.body.name},
$push: {messages: message}
},
options = {upsert: true};
Model.findOneAndUpdate(query, update, options, function (err, data) {
if (err) {
return res.status(500).send(err);
}
if (!data) {
return res.status(404).end();
}
return res.status(200).send(data);
});

Resources