I don't seem to be able to understand why data is missing. Here is my field:
<ReferenceField source="imageId" reference="files">
<TextField source="urlPath" />
</ReferenceField>
Here is what the main model papers is returning:
brandId: "b3dc4246-3adf-4f4e-9f81-294f23d24977"
brandModelId: "2819360d-2e6e-48e4-88a3-823a03177a7c"
colorId: "98031f74-a83f-4389-b9d6-bab959ff8a0e"
gsm: 200
id: "058cc13d-1255-4d9b-9ccf-e087ddb3935d"
imageId: "1bc99717-f60c-485e-989a-5d1726501b8d"
originalSize: "A4"
paperMaterialId: "5afd0d7c-5ace-49e3-a538-894ce288fe71"
paperSurfaceId: null
price: 12
ref: "44202129"
storeId: "2f7567ad-fa62-4bda-851b-89b8c39a189e"
Here is what the related model files is returning:
id: "1bc99717-f60c-485e-989a-5d1726501b8d"
originalFileName: "palette1.2.png"
type: "undefined"
urlPath: "asdf/101d7c68-9bf4-4d55-bbb5-818f62c480a3-palette1.2.png"
but here is my screenshot with missing data:
Note that the Store related field works, which has the exact same code:
<ReferenceField source="storeId" reference="stores">
<TextField source="name" />
</ReferenceField>
Also, here is my getMany dataProvider:
getMany: (resource, params) => {
const query = {
filter: JSON.stringify({ id: params.ids }),
};
const url = `${API_URL_LOCAL}/${resource}?${stringify(query)}`;
return request(url, 'get', {}, {}).then(({ data }) => ({
data,
}));
},
Any help is welcome.
In cases where you have some reference fields working, and others not, and no error is logged in console, this probably means that the non-working reference resource wasn't declared as Admin child with <Resource name="files" .../>.
From react-admin docs:
Note: You must add a <Resource> for the reference resource -
react-admin needs it to fetch the reference data. You can omit the
list prop in this reference if you want to hide it in the sidebar
menu.
https://marmelab.com/react-admin/Fields.html#referencefield
If none reference resource is working, and the resources has been properly declared, the error will probably rely on your data provider. Log the responses for getMany requests and see if it match the expected format and if it contains any data and not an empty array:
getMany { data: {Record[]}, validUntil?: {Date} }
A {Record} is an object literal with at least an id property, e.g. {
id: 123, title: "hello, world" }.
The validUntil field in the response is optional. It enables the
Application cache, a client-side optimization to speed up rendering
and reduce network traffic
https://marmelab.com/react-admin/DataProviders.html#writing-your-own-data-provider
Related
I'm using Sanity.io, GatsbyJS 3.x
Watch mode works great when you update content in the CMS, except for when the content you edit is part of a referenced schema of type 'document'.
Put another way, changes made to a document referenced by another document will not re-render the page despite having watch mode on and configured properly.
For example, here is a snippet from my Page schema.
...
{
name: "content",
type: "array",
title: "Page Sections",
description: "Add, edit, and reorder sections",
of: [
{
type: 'reference',
to: [
{ type: 'nav' },
{ type: 'section' },
{ type: 'footer' }
]
}
],
},
...
The above schema references a
nav schema
section schema
footer schema
Each of these are type 'document'.
See the example below.
export default {
type: 'document',
name: 'section',
title: 'Page Sections',
fields: [
{
name: 'meta',
title: 'Section Meta Data',
type: 'meta'
},
...
I want to reference a document, rather than an object, because I need to use the content created based on these schemas to be re-used in throughout the application.
Finally, I've configured the source plugin correctly for watch mode.
Gatsby Config is set properly
{
resolve: `gatsby-source-sanity`,
options: {
projectId: `asdfasdf`,
dataset: `template`,
watchMode: true,
overlayDrafts: true,
token: process.env.MY_SANITY_TOKEN,
},
},
In the CMS / Studio, when you edit one of the fields, you can see Gatsby re-compile in dev mode from the terminal. However, the page does not auto reload and display the changes made to the referenced document.
I've tried reloading the page with the reload button and via hard refresh, the changes do not render.
The only way to render the changes is to go back to the CMS and edit a field on the main “Page” document. Then it refreshes immediately.
Am I doing something wrong? Is this expected behavior? Is there a way to get this to work?
For those that run across this issue, I was able to answer my own question. I hope this saves you the day's it took me to find a solution.
Solution TLDR
You need to explicitly query the referenced document in order for watch mode to work properly.
Details with Examples
Summary
The gatsby-source-sanity plugin provides convenience queries that start with _raw for array types. When you use the _raw query in your GraphQL query, it will not trigger watch mode to reload the data. You need to explicitly query the referenced document in order for watch mode to work properly. This may have to do with how the plugin sets up listeners and I don't know if this is a bug or a feature.
Example
My Page Document has the following schema
{
name: "content",
type: "array",
title: "Page Sections",
description: "Add, edit, and reorder sections",
of: [
{
type: "reference",
to: [
{ type: "nav" },
{ type: 'section' },
],
},
],
},
The section is a reference to a section document.
{ type: 'section' }
The reason I'm not using an object is because I want the page sections to be re-usable on multiple pages.
Assuming you have watch mode enabled properly in your gatsby-config.js file, watch mode, like so...
// gatsby-config.js
{
resolve: `gatsby-source-sanity`,
options: {
projectId: `asdf123sg`,
dataset: `datasetname`,
watchMode: true,
overlayDrafts: true,
token: process.env.SANITY_TOKEN,
},
},
Then you should see the following behavior:
listen for document/content updates
re-run queries, update the data, hot-reload the page
You'll see the following scroll in your terminal window.
success Re-building development bundle - 1.371s
success building schema - 0.420s
success createPages - 0.020s
info Total nodes: 64, SitePage nodes: 9 (use --verbose for breakdown)
success Checking for changed pages - 0.001s
success update schema - 0.081s
success onPreExtractQueries - 0.006s
success extract queries from components - 0.223s
success write out requires - 0.002s
success run page queries - 0.010s - 1/1 99.82/s
This works great if you are querying the main document or any referenced objects. However, if you are querying any references to another document then there is one gotcha you need to be aware of.
The Gotcha
When you use the _raw query in your GraphQL query, it will not trigger watch mode to reload the data. You need to explicitly query the referenced document in order for watch mode to work properly.
Example: This Query will NOT work
export const PageQuery = graphql`
fragment PageInfo on SanityPage {
_id
_key
_updatedAt
_rawContent(resolveReferences: {maxDepth: 10})
}
`
Example: This query WILL Work
export const PageQuery = graphql`
fragment PageInfo on SanityPage {
_id
_key
_updatedAt
_rawContent(resolveReferences: {maxDepth: 10})
content {
... on SanitySection {
id
}
}
}
`
This additional query is the key
Here is where I am explicitly querying the document that is being referenced in the 'content' array.
content {
... on SanitySection {
id
}
}
You don't actually need to use the data that results from that query, you simply need to include this in your query.
My guess is that this informs the gatsby-source-sanity plugin to set up a listener, whereas the _rawContent fragment does not.
Not sure if this is a feature, bug, or just expected behavior. At the time of writing the versions were as follows.
"gatsby": "3.5.1",
"gatsby-source-sanity": "^7.0.0",
Context:
I wrote an API to handle my user/group/permissions in api-platform, very similar to what we used to have in Symfony 1.4 sfGuardUserPlugin - except that the userGroup association have additional properties:
Exemple of a fake user get request:
{
"#context": "/contexts/User",
"#id": "/users/92",
"#type": "User",
"id": 92,
"firstName": "Joseph",
"lastName": "Chauvet",
"email": "Joseph.Chauvet#fakemail.fr",
"isActive": true,
"userGroups": [
{
"#id": "/user_groups/group=17;user=92",
"#type": "UserGroup",
"group": {
"#id": "/groups/17",
"#type": "Group",
"id": 17,
"name": "adsi",
"role": "ROLE_ADSI"
},
"isManual": false,
"isForbidden": false,
"groupname": "adsi"
}
]
}
sidenote: the groupname property in the userGroup is a virtual property using a getter, not redundant data.
The issue:
I've been trying very hard to get a working UserEdit in react-admin that could handle the Api described above (which is exactly what I wanted, so no issue here).
In order to map the data structure of the users -> userGroups -> groups ressources, I think I need an ArrayInput to iterate the userGroups and a ReferenceInput + selectInput to select the group value.
The list of groups resource in the selectInput is good, but the user's group current value is never shown, so I guess that the issue is with my referenceInput source attribute value.
I've tried many, many, many combinations, using
- getSource()
- adding <FormDataConsumer> Element
- trying to use a direct hook of useGetOne() even if I don't get very well how to the hooks works and that hooks and callback don't play very nicely together.
- even trying to set source "the hard way" like {"userGroups[0].group"} or trying a {"scopedFormData.group"} in the formDataConsumer. but nothing works...
Here's an exemple of one of my (most simple) attempt
export const UserEdit = props => (
<Edit {...props}>
<SimpleForm>
<TextInput source={"firstName"}/>
<TextInput source={"lastName"}/>
<TextInput source={"email"} type="email"/>
<DateInput source={"lastLogin"}/>
<BooleanInput source={"isActive"}/>
<ArrayInput source={"userGroups"}>
<SimpleFormIterator>
<ReferenceInput
source={"group"} //should translate to userGroups[0].group
reference={"groups"}
label={"Groupe"}
>
<SelectInput label={"Groupe"} optionText="name"/>
</ReferenceInput>
</SimpleFormIterator>
</ArrayInput>
</SimpleForm>
</Edit>
)
I've alors tried this way:
[...]
<ArrayInput source={"userGroups"}>
<SimpleFormIterator>
<FormDataConsumer>
{({
formData, // The whole form data
scopedFormData, // The data for this item of the ArrayInput
getSource, // A function to get the valid source inside an ArrayInput
...props
}) =>
<ReferenceInput
//source={useGetOne('groups', '/groups/16').data}
source={getSource('group')}
reference={"groups"}
label={"Groupe"}
>
<SelectInput label={"Groupe"} optionText="name"/>
</ReferenceInput>
}
</FormDataConsumer>
</SimpleFormIterator>
I've also tried the useGetOne hook with to fetch data with a direct reference, but the hooks is forbidden in the callback, and I don't understand much of premises in jsx context.
Useless self context: I'm a senior developper new to the whole react/redux paradigm and I'm used to solve problem myself, but I'm stuck on this and spent something like fifty hours to study documentation, bug, sources to get rid of this.
My Api-platform stack and react-admin are up-to-date (yes even react-admin today release 3.2.4 that fix an arrayInput bug)
My uneducated wilds guess is that:
I'm just plainly doing it wrong
This may be related to the way the HydraProvider parse and store/cache references of related resources instead of passing it to react elements like the simpleRestProvider does.
It may be related, neither me nor my other teammates succeeded to use source={"reference.property"} in any input/field in react-admin using the Hydra dataProvider.
Any help is very very appreciated, one of my teammate added: "Kévin (ie: Dunglas), viens mettre des paillettes dans ma vie" 🤣
I am using react-native-webview. I am adding static HTML content into it, with baseUrl for relative path.
{
console.log(navState);
}}
/>
on Clicking a link there in Webview, getting the below log:
{
canGoForward: false,
canGoBack: false,
title: "",
loading: true,
url: "data:text/html;charset=utf-8;base64,", …}
How to get the exact url? I'm trying this on Android Emulator
Assuming that you're referring to navState object of the onNavigationStateChange function when you did the following:
onNavigationStateChange={navState => { //some logic }}
The navState object includes these properties:
canGoBack
canGoForward
loading
navigationType
target
title
url
In fact, you console logged the navState object.
To get the URL, use navState.url. Hence console.log(navState.url) would log the URL.
Note: This method will not be invoked on hash URL changes. See this.
Hey i've built a Nuxt app and am having trouble with the package #nuxtjs/google-tag-manager package. Found below. The documentation is pretty light and I haven't found many example implementations out there. In my nuxt.config.js I have the following set.
['#nuxtjs/google-tag-manager', {
id: process.env.GTM_ID,
layer: 'dataLayer',
pageTracking: true
}],
..but unfortunately am not getting any Page Views in Google Tag Manager
Does anyone have any ideas or experience in how to best implement GTM or what has worked for them?
Thanks in advance
I had a look at the package, inside https://github.com/nuxt-community/gtm-module/blob/master/lib/defaults.js there is this piece of code:
function startPageTracking(ctx) {
ctx.app.router.afterEach((to) => {
setTimeout(() => {
ctx.$gtm.push(to.gtm || {
routeName: to.name,
pageType: 'PageView',
pageUrl: '<%= options.routerBase %>' + to.fullPath,
pageTitle: (typeof document !== 'undefined' && document.title) || '',
event: '<%= options.pageViewEventName %>'
})
}, 250)
})
}
From that, it looks like the datalayer looks like this:
{
routeName: to.name,
pageType: 'PageView',
pageUrl: '<%= options.routerBase %>' + to.fullPath,
pageTitle: (typeof document !== 'undefined' && document.title) || '',
event: '<%= options.pageViewEventName %>' //default is 'nuxtRoute'
}
The default name for the event is 'nuxtRoute'. Thus in GTM, you'll get to define a custom event trigger to trigger on the "nuxtRoute' event. Like so:
Then you want to create two DataLayer variables in GTM that will capture pageUrl(Please note the camel case) and possibly routeName, I say routeName is optional depends on if you're changing/updating the of the document or not.
Then create your Google Analytics tag in GTM. Make sure it is the "pageview" type, then check the "enable overriding settings in this tag" checkbox, under "more settings > fields to set" type in "page" for fieldname and for "value" reference that variable we created. If you want to set the page title using the to.name variable just use the "title" field. Make sure you add the nuxt route trigger as well under "triggering".
Save and publish everything or run it in preview mode and you should see the pageviews some through.
I have a Vue app that generates as many forms as the user likes. Each form represent a 'risk' in this 'risks' data model:
var app = new Vue({
el: '#app',
data: {
risks: [
{
'risk_name': '', 'fields': [{'field': '', 'type': ''}], 'errors':[]
}
],
},
Then I send the 'risks' to a post API request. Then I make a second get request that brings back a template with the processed data as JSON. So far so good. However, how do I render the template (new page) with this data.
This is how I receive the data:
Submit() {
if (this.checkValidity()) {
console.info(this.risks)
axios.post('risks', this.risks)
.then(function(response) {
axios.get('risks')//This gets the data back
})
.catch(function(error) {
alert('This Error occured: ' + error)
})
}
},
This is the GET API call that I called above (Flask):
#app.route('/risks', methods=['GET'])
def getInsurancePolicyForm():
risks = getTables(app.metadata)
return render_template('form.html', risks=risks)
I can see the rendered template in developer tools, but the page the user sees remains the same.
Your page should be focussed on the risks object instead of the response from the API call.
When the user selects options, add them to the risks data object so your risks object looks like:
risks: [
{
'risk_name': '', 'fields': [{'field': 'My Field', 'type': 'text', modelToHoldData: ''}], 'errors':[]
}
]
Then, when the user clicks on Submit, by all means send it to the API but you don't need a template from there, use the risks object like so (remembering to hide the form they've just chosen their fields with):
<div v-for="risk in risks">
<label>{{risk.field}}</label>
<input type="text" v-if="risk.type === 'text'" v-model="risk.modelToHoldData" />
</div>
You can adjust the above example to add as many variations of the example as you allow the user to select.
Once all this is done and you need access to the data the user enters, you can loop through the array again and just access the modelToHoldData prop on each of the risks.