Vulcan 1.15
I’m going to guess that the past few weeks have sucked for pretty much everybody reading this. But in a way, open-source software can be a unexpected escape from the daily grind.
Fixing bugs and refactoring React forms might not sound like the sexiest thing ever, but it’s a nice way to feel like you’re making progress on something and find a little bit of normality in a constantly changing world!
Here’s what’s new in the latest Vulcan release.
Field-Specific Data Loading
A big change in Vulcan 1.15 is how field-specific data loading is handled. This is the kind of data loading that happens when you need to load info related to a specific form field.
For example, a post’s categoriesIds
field might require loading a list of all available categories so that the user can pick one of them without having to manually type in its _id
.
Previously, this was achieved by assigning a query fragment to each field, then aggregating all these fragments together into one big query that ran when the form was first loaded.
Starting in Vulcan 1.15, each form field is responsible for its own data loading thanks to a new FormComponentLoader
component.
While this is less efficient in theory since it replaces a single big query with many small ones, thanks to Apollo’s batching this isn’t really a factor in practice. And it makes the overall architecture much easier to reason about.
BREAKING CHANGE: the query
property’s content must now specify a query name.
Before:
postsIds: {
query: `
posts {
_id
title
}
`
}
Now:
postsIds: {
query: `
query PostsQuery {
posts {
_id
title
}
}
`
}
Note that the query can accept one variable, the field’s value:
postsIds: {
query: `
query PostsQuery($value: [String!]) {
posts(input: { filter: { _id: { _in: $value } } }) {
_id
title
}
}
`
}
Handling Empty Variables
Because GraphQL will throw an error if a variable is defined in a query but not provided, you can also pass a function to the query
property and have that function return undefined
if the value is not defined, which will abort the query:
postsIds: {
query: ({ value }) => value && `
PostsQuery($value: [String!]) {
posts(input: { filter: { _id: { _in: $value } } }) {
_id
title
}
}
`
}
A common use case would be a “new document” form, where form field values will typically be empty. By following the above pattern the query will never run in that scenario.
A note about options
Because of how field-specific data loading is now handled, options
function cannot expect to receive data. Watch out for undefined
errors:
Wrong:
options: props => props.data.products.results.map(product => ({
value: product._id,
label: product.name,
})), // props.data may be undefined
Right (using Lodash’s get):
options: props => _.get(props, 'data.products.results', []).map(product => ({
value: product._id,
label: product.name,
})),
Field Decorators
Starting in Vulcan 1.15, we’re trying out a new experimental pattern that we’re calling field decorators.
These are basically “shortcut” functions that you can call on fields to automatically assign the right properties to them for a given input type.
For example, here’s how to use the new makeAutocomplete
field decorator to implement the new autocomplete
input type:
import { makeAutocomplete } from 'meteor/vulcan:core';const mySchema = makeAutocomplete({
fieldFoo: {
optional: true,
// etc.
}
}, options)
In this case, the second options
argument requires the following property:
autocompletePropertyName
: the name of the property on which to run the autocomplete (e.g.label
,title
,name
, etc.).
If the field is a relation
field (e.g. field.resolveAs.relation
is defined), makeAutocomplete
will try to guess the correct resolver to use based on the relation. If not, you can also manually specify this option:
queryResolverName
: the name of themulti
resolver that will be queried to get the required data (e.g.movies
,posts
, etc.).
Given those options, makeAutocomplete
will add the correct input type as well as generate the queries needed both to load previously selected options and autocomplete suggestions.
Note that this is still a bit experimental and might change in the future based on the feedback we receive. Let us know if this is a good pattern or if you’d prefer something more obvious (but maybe also more “magical”?) like input: "autocomplete"
.
Easier Schemas with createSchema()
Vulcan 1.15 introduces the new createSchema()
function. This is a smaller wrapper that takes a Vulcan schema and “converts” it into a SimpleSchema schema.
The arrayItem Property
Having this middle layer lets us be a bit smarter about some things. For example, the new arrayItem
property lets you specify the type and shape of an array’s items without having to create a separate foo.$
field.
Before:
const mySchema = {
fooArray: {
type: Array,
// ...
},
'fooArray.$': {
type: String,
// ...
}
}
Now:
const mySchema = createSchema({
fooArray: {
type: Array,
arrayItem: {
type: String,
// ...
},
// ...
}
});
Other Improvements
Improved MutationButton
The MutationButton
component will now show you any GraphQL errors resulting from the mutation in a small tooltip above the button.
Meteor 1.10.2
We now officially support the latest version of Meteor.
GraphQL Schema Logging
Your app’s GraphQL schema is now automatically logged out to a schema.graphql
file at the root of your project every time your code changes.
Update Troubleshooting
If you run into update troubles, let us know in the Slack chatroom.
One common problem is having outdated versions of Meteor packages. Make sure the meteor:apollo
package in particular is at version 3.1.0
.