MST: define model with children of itself - mobx-state-tree

Just started to fiddle around with Mobx-state-tree.
I have this model, that has parent and children properties that can be filled with instances of the same model.
So basically something like this:
const Page = types.model({
guid: types.string,
title: types.string,
description: types.string,
parent: types.union(Page, types.undefined),
children: types.array(Page),
})
But, obviously Page is not available yet on the creation of this model.
How can I do this?

After minutely reading the docs, I found the answer. Using types.late():
const Page = types.model({
guid: types.string,
title: types.string,
description: types.string,
parent: types.union(types.late(() => Page), types.undefined),
children: children: types.optional(types.array(types.late(() => Page)), []),
})

Related

Error: Expected [object Object] to be a possible input type, saw GraphQLObjectType(Category)

I am trying to get trying to create a Product with the following fields, however whenever I try to have my categories object within my Product Object equal an actual Category Object that is already made I get the error above.
I want to have an [Category] be the value of the categories field within my Product Object, I hope that makes sense.
import {Category} from "./Category"
export const ProductMutation = extendType({
type: "Mutation",
definition(t) {
//create a new item
t.nonNull.field('createProduct', {
type:'Product',
args: {
id: stringArg(),
name: nonNull(stringArg()),
description: (stringArg()),
ingredients: list(stringArg()),
img: (stringArg()),
moveActive: (booleanArg()),
price: (floatArg()),
category: list(stringArg()), // this needs to be type Category and then i should be good
//tried category: list(arg({ type: Category})), (this didn't work)
},
resolve(_root, args, ctx) {
return ctx.prisma.product.create({
data: {
name: args.name,
description: args.description,
ingredients: args.ingredients,
img: args.img,
moveActive: args.moveActive,
price: args.price,
category: args. category
}
})
}
})
}
})

serving images with graphql

I have graphql server built by express-graphql and I use mongoDb to store images in normal database collection since they are < 16MB.
I have react and android app. What is the best way to serve these images to the clients.
In my schema I have something like this.
const productSchema = new Schema({
views: {type: Number, default: 0},
//Storing only thumbnail in this document.
thumbnail: {data: Buffer, contentType: String},
});
And in my schema I have something like
const productType = new GraphQLObjectType({
name: 'Product',
fields: () => ({
//.........
thumbnail: {type: 'WHAT TO DO HERE'},
views: {type: new GraphQLNonNull(GraphQLInt)}
//.........
}),
});
Edit:
I created a router like this.
router.get('/t/:productId', function (req, res) {
const productId = req.params.productId;
Product.findById(productId, {thumbnail: true}).then(thumbnail => {
res.contentType(thumbnail.contentType);
res.end(thumbnail.data, "binary");
}).catch(e => {
console.error(e);
res.sendStatus(404);
});
});
GraphQL.js responses are serialized as JSON, which means you can't serve images with it. You can return either a URL or a Base64-encoded string representing the image, as described in this answer. Either way the type for your field would be GraphQLString.

Attempting to access a types.reference(Model) before it's id is set for mobx-state-tree?

Got a question. So I'm making a small app working with the github api (using mobx-state-tree and mobx). I'm brand new to both.
I'm having a problem: you see selectedRepo? If i set an id to it, I can access selectedRepo. However... if I attempt to access selectedRepo before I set an id to it, the application will break. I've tried wrapping it with types.optional, types.maybe, but no matter what it just will not work.
Any ideas?
const defaultState = {
count: 10,
githubToken: "",
selectedRepo: ""
};
const Issue = types.model({
id: types.identifier(types.number),
title: types.string,
body: types.string
});
const Repo = types.model({
id: types.identifier(types.number),
name: types.optional(types.string, ""),
owner: types.optional(
types.model({
login: ""
}),
{}
)
});
const AppStore = types
.model({
state: "done",
githubToken: "",
issues: types.optional(types.array(Issue), []),
repos: types.optional(types.array(Repo), []),
selectedRepo: types.reference(Repo)
})

GraphQL : Implementing windowed pagination for regular list

I'm trying to implement a windowed pagination using a "List". I don't need the cursor based solution with connections, because I need to show numbered pages to the user.
There are "User" and "Post" objects."User" has one-to-many relation to "Post".
Using graphql-js for schema,
here is my schema for userType and postType:
var userType = new GraphQLObjectType({
name: 'User',
fields: () => ({
id: globalIdField('User'),
posts: {
type: new GraphQLList(postType),
args: {
page:{
type: GraphQLInt,
defaultValue: 0
}
},
resolve: (_, args) => {
//code to return relevant result set
},
},
totalPosts:{
type: GraphQLInt,
resolve: () => {
//code to return total count
}
},
}),
interfaces: [nodeInterface],
});
var postType = new GraphQLObjectType({
name: 'Post',
fields: () => ({
id: globalIdField('Post'),
name: {type: GraphQLString},
//other fields
}),
interfaces: [nodeInterface],
});
Please notice the "totalPosts" field in "userType". Since there is going to be other Lists for the user,with the same paging needs, I'm going to end up maintaining lot of "total{Type}" variables in the fragment. This can be solved if I can send the totalCount within the List result somehow.
https://github.com/facebook/graphql/issues/4 this issue talks about implementing a wrapper over the List to include the totalCount in the result set.
I tried creating a wrapper like this:
var postList = new GraphQLObjectType({
name: 'PostList',
fields:()=>({
count: {
type: GraphQLInt,
resolve: ()=>getPosts().length //this is total count
},
edges: {
type: new GraphQLList(postType),
resolve: () => {
return getPosts() ; // this is results for the page, though I don't know how to use 'page' argument here
},
}
}),
interfaces: [nodeInterface],
});
but how should I connect this to the userType's posts field? And how can I use a 'page' argument on this wrapper, like I have in original userType?
how should I connect this to the userType's posts field? And how can I use a 'page' argument on this wrapper, like I have in original userType?
One simple way to implement what you're trying to do is to define a dumb wrapper type postList like this:
var postList = new GraphQLObjectType({
name: 'PostList',
fields:()=>({
count: { type: GraphQLInt },
edges: { type: new GraphQLList(postType) }
// Consider renaming 'edges'. In your case, it's a list, not a
// connection. So, it can cause confusion in the long run.
}),
});
Then in the userType definition, add a field of that wrapper type and define its resolve function like below. As for argument page, just describe it while defining the field type posts.
posts: {
type: postList,
args: {
page:{
type: GraphQLInt,
defaultValue: 0
},
...otherArgs
},
resolve: async (_, {page, ...otherArgs}) => {
// Get posts for the given page number.
const posts = await db.getPosts(page);
// Prepare a server-side object, which corresponds to GraphQL
// object type postList.
const postListObj = {
count: posts.length,
edges: posts
};
// Consider renaming 'edges'. In your case, it's a list, not a
// connection. So, it can cause confusion in the long run.
},
},

Adding custom records to a JSON data store for a combobox (ExtJS)

I have a JSON data store used for a combobox selection which is working fine, however I wish to add a custom record to the combobox and it isn't working. Here is my code:
Note: I removed some of the code to make it easier to read.
var ds = new Ext.data.Store({
proxy: new Ext.data.ScriptTagProxy({
url: 'ajax.aspx?type=CR'
}),
reader: new Ext.data.JsonReader({
root: 'topics',
totalProperty: 'totalCount',
id: 'clientId'
}, [
{name: 'name', mapping: 'clientName'},
{name: 'address', mapping: 'clientAddress'}
])
});
// add the Other record
// create a Record constructor from a description of the fields
var TopicRecord = Ext.data.Record.create([ // creates a subclass of Ext.data.Record
{name: "id"},
{name: 'name'},
{name: 'address'}
]);
// create Record instance
var myNewRecord = new TopicRecord({
id: Ext.id(),
name: 'Other',
address: 'Other'
});
ds.add(myNewRecord);
ds.commitChanges();
var carrierSearch = new Ext.form.ComboBox({
// removed some code here
onSelect: function(record){
carrierSearch.setValue(record.data.name);
document.getElementById(carrierIdHidden).value=record.id;
fieldOnBlurPost(document.getElementById(id), page, true);
carrierSearch.collapse();
}
});
Any ideas on why the section under "// add the Other record" (up until ds.commitChanges();) isn't adding my custom record?
Thanks,
Domenic
First of all I would like to say O- H !!
From the ExtJS documentation for data.Store.add:
add( Ext.data.Record[] records ) : void
Add Records to the Store and fires the add event. To add Records to the store from a remote source use load({add:true}). See also recordType and insert.
I'm assuming that your ComboBox is using mode : 'remote'. I believe that you'll need to use the load method and feed it the {add:true} there.
I have never tried this but from the documention I would think you don't need to create the record Type again using Ext.data.Record.Create() since the datastore is provided a reader it should be available in the store. In fact, my guess is that the id property on the record type creates a different type of record object that does not match the store configuration, so it causes the add function to fail.
Try something like this:
var defaultData = {
name: 'Other Name'
address: 'Other Address'
};
var r = new ds.recordType(defaultData, Ext.id()); // create new record
ds.add(r);
I based this on the sample information found in the 'recordType' property of the Ext.data.Store class http://dev.sencha.com/deploy/dev/docs/?class=Ext.data.Store
When I added the record inside the data store it worked:
var ds = new Ext.data.Store({
proxy: new Ext.data.ScriptTagProxy({
url: 'ajax.aspx?type=CR&searchtype=begins'
}),
reader: new Ext.data.JsonReader({
root: 'topics',
totalProperty: 'totalCount',
id: 'clientId'
}, [
{name: 'name', mapping: 'clientName'},
{name: 'address', mapping: 'clientAddress'}
]),
listeners: {
load: function() {
// add the Other record
// create a Record constructor from a description of the fields
var TopicRecord = Ext.data.Record.create([ // creates a subclass of Ext.data.Record
{name: "clientId"},
{name: 'name'},
{name: 'address'}
]);
// create Record instance
var myNewRecord = new TopicRecord({
name: 'Other'
},'Other');
this.add(myNewRecord);
this.commitChanges();
}
}
});

Resources