I'm trying to catch info from GraphQL Gatsby with useStaticQuery but the data returned is undefined and I don't understand why because in my http://localhost:8000/___graphql I received the good information.
My code is not a page component it's a reason why I used Static Query
My code is like that:
import React from "react";
import { useStaticQuery, graphql} from "gatsby";
export default function MenuMD () {
const { data } = useStaticQuery(
graphql`
query {
allFile(filter: {sourceInstanceName: {eq: "markdown"}}) {
edges {
node {
childrenMarkdownRemark {
frontmatter {
slug
title
}
}
}
}
}
}
`
)
console.log('static data', data);
return<>Menu from MarkDown</>
}
the expected result from http://localhost:8000/___graphql is something like that:
{
"data": {
"allFile": {
"edges": [
{
"node": {
"childMarkdownRemark": {
"frontmatter": {
"slug": "/projet_m",
"title": "Projet M"
}
}
}
},
{
"node": {
"childMarkdownRemark": {
"frontmatter": {
"slug": "/projet_n",
"title": "Projet N"
}
}
}
}
]
}
},
"extensions": {}
}
May be there is a reason for this undefined return?
Assuming that the query is working in the GraphiQL environment and there's a "markdown" sourceInstanceName in your data structure, try it like this:
export default function MenuMD() {
const data = useStaticQuery(
graphql`
query {
allFile(filter: { sourceInstanceName: { eq: "markdown" } }) {
edges {
node {
childrenMarkdownRemark {
frontmatter {
slug
title
}
}
}
}
}
}
`
);
console.log("static data", data);
return <>Menu from MarkDown</>;
}
There's nothing wrong using a static query in a component rather than a page, the result must be exactly the same (in terms of output).
Your data should be inside data.allFile.edges (or d'estructures directly as { allFile }) not directly destructuring the result of the static query as data. That's why data was undefined in your case.
Related
I have the following JSON data:
[
{
"taxonomy_slug": "product_cat",
"taxonomy_name": "Categories",
"frontend_slug": "product-category"
},
{
"taxonomy_slug": "product_tag",
"taxonomy_name": "Tags",
"frontend_slug": "product-tag"
},
{
"taxonomy_slug": "gift_box_size",
"taxonomy_name": "Gift Box Sizes",
"frontend_slug": "gift_box_size"
},
{
"taxonomy_slug": "product-type",
"taxonomy_name": "Type",
"frontend_slug": "product-type"
}
]
So for each taxonomy front-end slug, I want to generate a page for each taxonomy.
The URLs I want to generate are as follows:
product-category/{category_item_slug}
product-tag/{tag_item_slug}
gift_box_size/{gift_box_size_item_slug}
product-type/{product-type_item_slug}
Now each taxonomy has its own list of items and if it doesn't exist in the Wordpress admin, I want to return 404 page not found.
I have the following file/folder structure
|-> pages
|-> [taxonomy-frontend-slug]
|-> [taxonomy-item-slug].js
[taxonomy-item-slug].js file
import { useRouter } from 'next/router'
import productTaxonomy from '#gb-data/productTaxonomy.json'
export async function getStaticPaths() {
const paths = productTaxonomy.map((taxonomyData) => {
return {
params: { "taxonomy-frontend-slug": `/${taxonomyData.frontend_slug}` }
}
})
console.log('getStaticPaths', paths)
return {
paths: paths,
fallback: false,
}
}
export async function getStaticProps(context) {
console.log('getStaticProps context', context)
return {
props: {}
}
}
export default function TaxonomyItemPage() {
const router = useRouter()
console.log('router.query', router.query);
return (
<div>
TaxonomyItemPage
</div>
)
}
I see 2 options:
Do not return non-existent taxonomies from getStaticPaths. With fallback: false, users will get 404 for any paths not returned by this function.
Something like that should do the trick:
export async function getStaticPaths() {
const taxonomies = await getListOfTaxonomiesFromWordpress();
const paths = taxonomies.map((taxonomyData) => {
return {
params: { "taxonomy-frontend-slug": taxonomyData.frontend_slug }
}
})
return {
paths: paths,
fallback: false,
}
}
When loading static props for a path (provided you don't have getStaticPaths function like in 1), return the following from getStaticProps if data is not available in Wordpress in order to get 404:
return {
notFound: true
};
So try the following:
export async function getStaticProps({ params }) {
const taxonomyData = await getTaxonomyDataBySlugFromWordpress(params['taxonomy-frontend-slug']);
if (taxonomyData) {
return {
props: { taxonomyData }
}
}
return {
notFound: true
}
}
I am using nuxt and apollo together with: https://github.com/nuxt-community/apollo-module
I have a working GraphQL query (tested in GraphiQL):
(Because I want to fetch the info about my page and also some general SEO information)
{
entries(section: [pages], slug: "my-page-slug") {
slug
title
}
seomatic(uri: "/") {
metaTitleContainer
metaTagContainer
metaLinkContainer
metaScriptContainer
metaJsonLdContainer
}
}
I want to fetch this data as well with apollo in nuxt:
So I tried:
<script>
import page from '~/apollo/queries/page'
import seomatic from '~/apollo/queries/seomatic'
export default {
apollo: {
entries: {
query: page,
prefetch: ({ route }) => ({ slug: route.params.slug }),
variables() {
return { slug: this.$route.params.slug }
}
},
seomatic: {
query: seomatic,
prefetch: true
}
},
…
If I do that I will get an error message:
GraphQL error: Cannot query field "seomatic" on type "Query".
I then found this issue
https://github.com/apollographql/apollo-tooling/issues/648
and I would like to know if ths could be a problem of the apollo nuxt module.
Because following that fix indicated in the issue does not resolve anything.
I further tried to combine the two calls into one:
fragment SeoMaticFragment on Root {
seomatic(uri: "/") {
metaTitleContainer
metaTagContainer
metaLinkContainer
metaScriptContainer
metaJsonLdContainer
}
}
query myQuery($slug: String!) {
entries(section: [pages], slug: $slug) {
slug
title
}
SeoMaticFragment
}
~/apollo/queries/page.gql
But this would first throw an error
fragment Unknown type "Root"
So what is the best way to combine?
Why are the requests failing
is there an option to activate batching like described here: https://blog.apollographql.com/query-batching-in-apollo-63acfd859862
-
const client = new ApolloClient({
// ... other options ...
shouldBatch: true,
});
thank you so much in advance.
Cheers
There is actually a solution to this problem.
I found out that the result hook in vue-apollo solves this problem:
Example code that works:
<script>
import gql from 'graphql-tag'
const query = gql`
{
entries(section: [pages], slug: "my-example-page-slug") {
slug
title
}
seomatic(uri: "/") {
metaTitleContainer
metaTagContainer
metaLinkContainer
metaJsonLdContainer
}
}
`
export default {
data: () => {
return {
page: false,
seomatic: {}
}
},
apollo: {
entries: {
query,
prefetch: ({ route }) => ({ slug: route.params.slug }),
variables() {
return { slug: this.$route.params.slug }
}
},
result(result) {
this.entries = result.data.entries
this.seomatic = result.data.seomatic
}
}
}
</script>
The Vuex store of my Vue.js application is growing and getting a little bit messy with a lot of constants in it. I would like to split these constants in separate files and import them into my Vuex store store.js. I'm new to JavaScript so I would like to know:
How to store these constants in separate files? What would be the syntax in these files?
How to import these constants in store.js? What would be the exact syntax to do so?
Here is the current content of my store.js
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export const store = new Vuex.Store({
state: {
graphqlUrl: 'https://localhost/listof/api/v1/graphql',
errorObject: {
flag: false,
message: ''
},
// Data types queries
queryGetAllDataTypes: `query getAllDataTypes {
allSysDataTypes(orderBy: NAME_ASC) {
nodes {
id
name
}
}
}`,
// Data for linked list & attributes drodpdown in attribute form
// Response labels must be formatted according to Treeselect requirements
queryGetLinkedLists: `query getAllLists {
allSysLists(orderBy: NAME_ASC) {
nodes {
id:nodeId
label:name
attributes:sysAttributesByListId {
children:nodes {
id
label:name
}
}
}
}
}`,
// Data for linked list & attributes drodpdown in value form
// Response labels must be formatted according to Treeselect requirements
queryGetLinkedListValues: `query getAllValues {
all<GraphQlListName> {
nodes {
id
label:<graphQlAttributeName>
}
}
}`,
// Lists queries and mutations
queryGetAllLists: `query getAllLists{
allSysLists(orderBy: NAME_ASC) {
nodes {
id
name
description
}
}
}`,
queryGetList: `query getList($id: Int!) {
sysListById(id: $id) {
id
name
description
tableName
sysAttributesByListId {
nodes {
id
name
description
flagMandatory
flagUnique
dataTypeId
sysDataTypeByDataTypeId { name }
linkedAttributeId
sysAttributeByLinkedAttributeId {
name
columnName
listId
sysListByListId {
name
tableName
}
}
columnName
}
}
}
}`,
mutationCreateList: `mutation createList($sysList: SysListInput!) {
createSysList(input: {sysList: $sysList}) {
sysList {
id
}
}
}`,
mutationUpdateList: `mutation updateList($id: Int!, $sysListPatch: SysListPatch!) {
updateSysListById(input: {id: $id, sysListPatch: $sysListPatch }) {
sysList {
id
}
}
}`,
mutationDeleteList: `mutation deleteList($id: Int!) {
deleteSysListById(input: {id: $id}){
sysList {
id
}
}
}`,
// Attributes queries and mutations
queryGetAttribute: `query getAttribute($id: Int!) {
sysAttributeById(id: $id) {
id
name
description
flagMandatory
flagUnique
dataTypeId
sysDataTypeByDataTypeId { name }
linkedAttributeId
sysAttributeByLinkedAttributeId {
name
listId
sysListByListId { name }
}
defaultValue
}
}`,
mutationCreateAttribute: `mutation createAttribute($sysAttribute: SysAttributeInput!) {
createSysAttribute(input: {sysAttribute: $sysAttribute}) {
sysAttribute {
id
}
}
}`,
mutationUpdateAttribute: `mutation updateAttribute($id: Int!, $sysAttributePatch: SysAttributePatch!) {
updateSysAttributeById(input: {id: $id, sysAttributePatch: $sysAttributePatch }) {
sysAttribute {
id
}
}
}`,
mutationDeleteAttribute: `mutation deleteAttribute($id: Int!) {
deleteSysAttributeById(input: {id: $id}){
sysAttribute {
id
}
}
}`,
// Generic query used as template to fetch all values from a list
queryGetAllValues: `query getAllValues {
all<GraphQlListName> {
nodes {
id
createdDate
updatedDate
<graphQlAttributeName>
}
}
}`,
// Generic query used as template to fetch one value from a list
queryGetValue: `query getValue($id: Int!) {
<graphQlListName>ById(id: $id) {
id
createdDate
updatedDate
<graphQlAttributeName>
}
}`,
// Generic mutation used as template to create a new value in a list
mutationCreateValue: `mutation createValue($<graphQlListName>: <GraphQlListName>Input!) {
create<GraphQlListName>(input: {<graphQlListName>: $<graphQlListName>}) {
<graphQlListName> {
id
}
}
}`,
// Generic mutation used as template to update a value in a list
mutationUpdateValue: `mutation updateValue($id: Int!, $<graphQlListName>Patch: <GraphQlListName>Patch!) {
update<GraphQlListName>ById(input: {id: $id, <graphQlListName>Patch: $<graphQlListName>Patch }) {
<graphQlListName> {
id
}
}
}`,
// Generic mutation used as template to delete a value in a list
mutationDeleteValue: `mutation deleteValue($id: Int!) {
delete<GraphQlListName>ById(input: {id: $id}){
<graphQlListName> {
id
}
}
}`,
}
});
The simplest option is to create a new file for constants (constants.js) and define and export them there, e.g.:
export const cat = 'black'
export const dog = 'brown'
export const mouse = 'grey'
Then either import them all into the current namespace in store.js:
import * as constants from './constants'
Or import them selectively when needed:
import { cat, dog } from './constants'
VueX stores are immutable by default and can only be mutated via mutations. Vuex has created modules for this. I simply create a module and put all my constants in there without mutations.
From the vux documentation:
https://vuex.vuejs.org/guide/modules.html below:
const moduleA = {
state: { ... },
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: { ... },
mutations: { ... },
actions: { ... }
}
const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
}
})
store.state.a // -> `moduleA`'s state
store.state.b // -> `moduleB`'s state
Using nuxt.js you can simply add constants.js to the store folder with:
export const state = () => ({
example: {}
});
I'm having trouble trying to read from apollo cache from within a react component the mutation works and writes to my server and returns the data but when passed to my update function it seems to lose the context of this when in inMemoryCache.js
"apollo-cache-inmemory": "^1.2.5"
"react-apollo": "^2.1.4"
"apollo-boost": "^0.1.7"
TypeError: Cannot read property 'read' of undefined
at ./node_modules/apollo-cache-inmemory/lib/inMemoryCache.js.InMemoryCache.readQuery
import React, { Component } from "react";
import { graphql } from "react-apollo";
import trim from "lodash/trim";
import AuthorForm from '../components/author-form';
import ALL_AUTHORS from "../graphql/getPosts.query";
import CREATE_AUTHOR from "../graphql/createAuthor.mutation";
class CreateAuthor extends Component {
state = {
errors: false
};
onSubmit(event) {
event.preventDefault();
const form = new FormData(event.target);
const data = {
firstName: form.get("firstName"),
lastName: form.get("lastName")
};
if (!data.firstName || !data.lastName) {
return this.setState({ errors: true });
}
this.create({
firstName: trim(data.firstName),
lastName: trim(data.lastName)
});
}
async create(variables) {
const { createAuthor } = this.props;
this.setState({ errors: false });
try {
await createAuthor({
variables,
update: (cache, data) => this.updateCache(cache, data)
})
} catch (e) {
this.setState({ errors: true })
}
}
updateCache({ readQuery, writeQuery }, { data: { createAuthor }, errors }) {
if (errors) {
return;
}
const { allAuthors } = readQuery({
query: ALL_AUTHORS,
defaults: {
allAuthors: []
}
});
/*eslint-disable*/ console.log(allAuthors);
}
render() {
return (
<div>
<AuthorForm onSubmit={this.onSubmit.bind(this)}/>
<OnError/>
</div>
);
}
}
export default graphql(CREATE_AUTHOR, { name: "createAuthor" })(CreateAuthor);
is it to do with me binding this to the onSubmit button? if so what is the proper way to attach a function to a element without losing context of this within the component and still allow apollo cache to function properly.
I was losing the context of this because I was deconstructing the first argument. this is what I settled on in the end.
It throw errors when there were no allAuthors on the ROOT_QUERY object so added it to my return statement.
this doesn't feel like an ideal way of updating the cache shouldn't default param passed to readQuery prevent the error thrown.
updateCache(cache, { data: { createAuthor }, errors }) {
if (errors || !cache.data.data.ROOT_QUERY.allAuthors) {
return;
}
const query = ALL_AUTHORS;
const { allAuthors } = cache.readQuery({
query,
defaults: {
allAuthors: []
}
});
const data = {
allAuthors: allAuthors.concat([createAuthor])
};
cache.writeQuery({
query,
data
});
}
I have the following code in typescript which calls a sharepoint rest api and gets lists items, however sharepoint internally saves thes user profiles with an id, instead of the name, so you can see number 9 actually should be a person name.
What I want is to replace that number 9 with my name, I already know what service I need to call, however I dont know how do it for each row of the json returned and to replace the 9 with the name returned.
My code is as follow:
/// <reference path="../../../typings/jquery/jquery.d.ts" />
/// <reference path="../../../typings/jquery.dataTables/jquery.dataTables.d.ts" />
import {
BaseClientSideWebPart,
IPropertyPaneSettings,
IWebPartContext,
PropertyPaneTextField
} from '#microsoft/sp-client-preview';
//import styles from './Pnpcrudsample.module.scss';
import ModuleLoader from '#microsoft/sp-module-loader';
import * as strings from 'pnpcrudsampleStrings';
import { IPnpcrudsampleWebPartProps } from './IPnpcrudsampleWebPartProps';
//import * as pnp from 'sp-pnp-js';
import MockHttpClient from './MockHttpClient';
import { EnvironmentType } from '#microsoft/sp-client-base';
require('jquery');
require('datatables');
export interface ISPLists {
value: ISPList[];
}
export interface ISPList {
Title?: string;
Id: number;
}
export interface IListItems{
value: IListItem[];
}
//Title,h7vv,v7nw,mczsId,mczsStringId,BooleanColumn
export interface IListItem {
Title: string;
h7vv: string;
v7nw: string;
mczsId: string;
BooleanColumn: string;
}
export default class PnpcrudsampleWebPart extends BaseClientSideWebPart<IPnpcrudsampleWebPartProps> {
//private container: JQuery;
//Default constructor, here we have to load css
public constructor(context: IWebPartContext) {
super(context);
ModuleLoader.loadCss('//cdn.datatables.net/1.10.12/css/jquery.dataTables.min.css');
}
///Gets data from the mock, fake data
private _getMockListData(): Promise<IListItems> {
return MockHttpClient.get(this.context.pageContext.web.absoluteUrl)
.then((data: IListItem[]) => {
var listData: IListItems = { value: data };
return listData;
}) as Promise<IListItems>;
}
///Checks if the environment is local, then we will load data from mock, if not from the list
private _renderListAsync(): void {
// Local environment
if (this.context.environment.type === EnvironmentType.Local) {
this._getMockListData().then((response) => {
this._renderList(response.value);
});
}
else{
this._getListData()
.then((response) => {
this._renderList(response.value);
});
}
}
//Title,h7vv,v7nw,mczsId,mczsStringId,BooleanColumn
///Render list on the datatable
private _renderList(items: IListItem[]): void {
$('#example').DataTable({
data: items,
columns: [
{ "data": "Title" },
{ "data": "h7vv" },
{ "data": "v7nw" },
{ "data": "mczsId" },
{ "data": "BooleanColumn" }
]
});
}
///Get list data
private _getListData(): Promise<IListItems> {
return this.context.httpClient.get(this.context.pageContext.web.absoluteUrl + `/_api/web/lists/getbytitle('Lista')/items?$select=Title,h7vv,v7nw,mczsId,mczsStringId,BooleanColumn`)
.then((response: Response) => {
return response.json();
});
}
/// Generar contenido HTML
public render(): void {
debugger;
ModuleLoader.loadCss('//cdn.datatables.net/1.10.12/css/jquery.dataTables.min.css');
if (this.renderedOnce === false) {
this.domElement.innerHTML = `<table id="example" class="display" cellspacing="0" width="100%">
<thead>
<tr>
<th>Title</th>
<th>NumberColumn</th>
<th>DateColumn</th>
<th>PersonColumn</th>
<th>BooleanColumn</th>
</tr>
</thead>
</table>`;
}
this._renderListAsync();
}
//Property pane fields
protected get propertyPaneSettings(): IPropertyPaneSettings {
return {
pages: [
{
header: {
description: strings.PropertyPaneDescription
},
groups: [
{
groupName: strings.BasicGroupName,
groupFields: [
PropertyPaneTextField('listName', {
label: strings.ListNameFieldLabel
})
]
}
]
}
]
};
}
}
import { IListItem } from './PnpcrudSampleWebPart';
export default class MockHttpClient {
//Title,h7vv,v7nw,mczsId,mczsStringId,BooleanColumn
private static _items: IListItem[] =
[
{ Title: 'Mock List', h7vv: '1',v7nw :'01-01-2016',mczsId:'Luis Esteban Valencia',BooleanColumn:'Yes' },
{ Title: 'Mock List2', h7vv: '1',v7nw :'01-01-2016',mczsId:'Luis Esteban Valencia',BooleanColumn:'Yes' },
];
public static get(restUrl: string, options?: any): Promise<IListItem[]> {
return new Promise<IListItem[]>((resolve) => {
resolve(MockHttpClient._items);
});
}
}
The importan piece of the code above is: the _renderlist method which gets the data and BINDS it to a datatable (jquery plugin), I guess its there where I should plug the code to call the other service which is something like this:
https://mytenant.sharepoint.com/sites/devsitesc1/_api/web/getuserbyid(9)/title
No need of 2 REST calls here. The REST API call should look like...
https://mytenant.sharepoint.com/sites/devsitesc1/_api/web/lists/getByTitle('YourListTitle')/items?$select=Title,NumberColumn,...,PersonColumn/Title&$expand=PersonColumn
if you have any filter, you can add "$filter=..." also