React-Admin: How do I best use nested "source" props - reactjs

I am attempting to get react-admin to run on top of the WordPress REST API.
Posts title response is:
{
"title": {
"rendered": "Test"
}
}
And while using react-admin components to edit post title:
<TextInput source="title.rendered" label="Title" validate={required()} />
The challenge happens when submitting the Edit Form:
// the request body becomes this...
{
"title": { "rendered": "Test edited" }
}
// But I need to pass it as follows to work with the REST API...
{
"title": "Test edited"
}
I appreciate any help on solving this problem.

My solution for that issue was trying to Extend WordPress REST API with custom rest fields
Adding a new field name title to modify the existing one.
function ra_get_title_unnest( $post, $field_name, $request ) {
return get_the_title( (int) $post['id'] );
}
function ra_add_title_unnest_to_api() {
register_rest_field( 'post',
'title',
['get_callback' => 'ra_get_title_unnest']
);
}
add_action( 'rest_api_init', 'ra_add_title_unnest_to_api' );
then the response body becomes :
// without a nested form and in a consistent way
{
"title": "Test edited"
}

Related

Use data from graphql response in React

I guess this is a simple issue, but I am stuck here for a while, so any advice may be helpful!
I have a react app and I am calling a GraphQL api (with apollo). Inside an arrow function component I have:
const [executeQuery, { data }] = useLazyQuery(GET_ALL_TASKS);
const findId = (step) => {
executeQuery({
variables: {
"query": {
"state": "CREATED",
"taskDefinitionId": "something"
}
}
})
}
The query is successful and in the browser inspect panel I get this as the graphql response:
{
"data" : {
"tasks" : [ {
"id" : "2251",
"name" : "some_name",
"__typename" : "Task"
} ]
}
}
In my code I want to use the retrieved id. How can I isolate the id from the response? When I am trying to access the data I get an undefined error.
Thank you!
Not sure why you are wrapping your executeQuery in a function.
The data will be part of the response so you can get it like this:
const {data, loading} = executeQuery({
variables: {
"query": {
"state": "CREATED",
"taskDefinitionId": "something"
}
}
})
// may also need to check for the error state
if(loading){
console.log("Loading...")
}else{
/// the ids seem to be an array of objects
const ids = data.tasks.map((task)=> task.id)
console.log(ids)
}
For anyone who may have the same problem, I realized it is a caching error happening in apollo client. I couldn't figure out the solution. However, I temporarily solved it by downgrading the apollo client dependency to version 3.2.5

Populate dropdown on React from JSON Data WebAPI

I have a react form that has several dropdowns and it was working, however, the WebAPI changed and now the data is being returned slightly different and now none of the dropdowns are populated.
The old JSON format was like:
{
"data":
[{
"id" : 1,
"name" : "Michelle Smith",
},
{
"id" : 2,
"name" : "Jenn Arnold"
}
]
}
the drop down binding is:
const [ admins, setAdmin] = useState([]);
useEffect(() => {
getAdmins();
},[]);
//calls a JS file that connects to the API using axios
const getAdmins = () => {
adminGroups.GetAllAdmins()
.then((response) => {
setAdmins(response.data.data)
}
}
return (
<select>
<option>...</option>
{admins.map(data => {
<option
value={admin.id}
>
{admins.name}
<option>
</select>
)
The new JSON format is:
[{
"id" : 1,
"name" : "Michelle Smith",
},
{
"id" : 2,
"name" : "Jenn Arnold"
}]
There is no parent "data" tag in the new format, what would've caused the drop downs to stop binding with the new format? When the page loads, I can see the API being called (under the network tab) and if I go to the URL I can see data, just not in the React App. Is there another way I should be binding the dropdown?
[I'm fairly new to React and converting an Access app over to the web using React as the UI]
If all that's changed is that the API you're using no longer wraps the array in data then just don't go looking for it in your response.data.data use,
Instead just use response.data like so
const getAdmins = () => {
adminGroups.GetAllAdmins()
.then((response) => {
setAdmins(response.data)
}
}
I got this working, just by shutting down VS and opening it back up again. Not sure if VS had something cached or not, but the API was returning data, just not the UI until I restarted it. Thanks all

React Admin how to resolve an url into a field

I'm new with react admin and I'm tring to figure out how can I resolve an url in an Custom field. I am usin Django Rest Framework as my backend application and it returns this:
{
"id": "PwybRVej1r3L2Ag7smAvpqW45076GzZd",
"unity": "http://localhost:8000/api/v1/unity/n7VzbMW25rYZLB17SZ9Rl8eXqE36QDxk/",
"url": "http://localhost:8000/api/v1/truck/PwybRVej1r3L2Ag7smAvpqW45076GzZd/",
"created": "2022-08-08T23:48:32.876117Z",
"modified": "2022-08-08T23:48:32.876117Z",
"license_plate": "ADS-8974"
},
And I'm using JWT as the authentication standard. In this way I found ra-data-django-rest-framework. For simple cases, the usage is simply straightforward. But I'm trying to create a field that can resolve the url("http://localhost:8000/api/v1/unity/n7VzbMW25rYZLB17SZ9Rl8eXqE36QDxk/") and returns the information I need. Does anyone know how it could be resolved?
I tried this without success:
import * as React from "react";
import { useRecordContext, useGetOne } from 'react-admin';
const UnityField = ({ source }) => {
const record = useRecordContext();
const unity = useGetOne(record[source]);
console.log(unity)
return record ? (
<a href={record[source]}>
{unity}
</a>
) : null;
}
export default UnityField;
React-admin provides an <UrlField>, does it work in your case?
<UrlField source="unity" />
See https://marmelab.com/react-admin/UrlField.html for details

Splitting the string url in reactjs

I am trying to call the backend api which results in the details of the specific product based on id for example the api is http://localhost:54729/api/product/1 the 1 in the api is the id of the specific product. I am using redux and axios to get the data from the api.
EDIT:
The response from the api from which I am getting is the following
[
{
"id": 0,
"title": "TestProductAzure",
"description": "<p>While working with the EF Core Code-First approach, we create the classes for our domain entities first. Later, we’ll create the database from our code by using migrations. This is the opposite of the Database-First approach where we design our database first and then create the classes which match our database design.In the EF Core Code-First approach, we have full control over the code and the database is just a store without any logic. This approach is helpful in situations where we are starting with the development of a new project and we don’t have a clear picture of how our database should look like yet.We don’t have to worry about creating and updating the database. We can just make the changes in our code and then sync everything easily with the database. The important thing to note is that the manual changes that we make to the database could be lost during migration. So we should make changes to the code only.</p>",
"owner": "Seeded Company",
"link": null,
"url": "http://localhost:54729/api/product/1",
"type": "Internal",
"rank": 5
},
{
"id": 0,
"title": "Support | example.com",
"description": null,
"owner": null,
"link": "/search/product?url=https://www.example.com/support/",
"url": "https://www.exampl.com/support/",
"type": "External",
"rank": 3
},
{
"id": 0,
"title": "TestProductForAzure",
"description": null,
"owner": "Seeded Company",
"link": null,
"url": "http://localhost:54729/api/product/3",
"type": "Internal",
"rank": 0
},
The above response is the response when someone search something with the keyword. Now what I want if the user clicks on the url that has the api like http://localhost:54729/api/product/ I want to check the last number of the api i.e. the id and get the response from that forexample, if the user clicks on http://localhost:54729/api/product/2 . I want to pass the id 2 to the function which will then get pass from action and axios.get give the response.
My Action look like this
export function detailsProduct(id) {
const token = userService.getToken();
console.log(token);
const api = `/product/${id}`;
axios
.get(apiBaseUrl + api, { headers: { Authorization: `Bearer ${token}` } })
.then(res => {
console.log("helloProductDetails", apiBaseUrl + api);
try {
store.dispatch({
type: PRODUCT_DETAILS,
payload: res
});
} catch (err) {
console.log("error", err);
}
});
My Reducer look like this:
case PRODUCT_DETAILS:
console.log(
"action",
action,
"actionPayload",
action.payload,
"state",
state
);
return {
...state,
products: action.payload,
loading: false,
totalRecords: action.payload.length
};
and my .jsx react look like this
function onClickHandler() {
console.log("calling Details Product, HardCodedly");
detailsProduct(1);
}
return (
<div className="row">
<div className="s130">
{/* <div class="col-lg-8 col-xs-8 col-sm-12 col-md-7"> */}
<div className="col-lg">
<div className="container">
{result.type === "Internal" ? (
<Link to={DoctorProductLocation.path} onClick={onClickHandler}>
<h3>{result.title}</h3>
</Link>
) : (
//Try With OnClick Function
<a href={result.url} target="_blank">
<h3>{result.title}</h3>
</a>
)});
As you did notice , I'm sending the id hardcoded for now just to check if I am getting the information, which is working fine. But All I want to do is that when I click on the response it will automatically check the id and send us the response of that.
I thought of splitting the url until the id and pass it to the function action. But I don't know How? Or else if you have any other good idea, I will be thankful to you.

How to set up admin-on-rest with couchDB?

Edit to the makers of AoR : Your framework suffers from horrid documentation. You should really focus on that, people would really adopt it then.
I cant for the life of me decipher how admin-on-rest does the 'rest' part. If there is a better framework with better documentation, Im open to that.
Im very new to react, so thats probably part of it.
What I can discern is that
1) The [Admin] tag takes a prop 'restClient', and this is a function that sets your base path to your JSON source, then returns a function with a specific signature (takes 3 arguments, returns a promise).
2) Then a [Resource] tag adds to the path with name="posts" and makes a list, which (heres where it turns to magic) basically does a wget to your database then iterates over the results.
What I want to do : hook up couchDB to admin-on-rest. I already have a few test docs made on localhost. The couchDB url looks like :
http://127.0.0.1:5984/myproject/_design/getclients/_view/getclient/
and this works in postman, giving me a json object like this :
{
"total_rows": 4,
"offset": 0,
"rows": [
{
"id": "afc3bb9218d1a5c1e81ab3cc9f004467",
"key": {
"status": "active",
"rating": 9.1,
"bio": {
"fname": "Sam",
"mname": "TestMName",
"lname": "TestLName",
"address": "712347 B Street",
"state": "CA",
"city": "Los Santos",
"zip": "90211",
"phone": "123-456-7890",
"email": "sam#samsemail.com",
"username": "TestSam",
"password": "abc123"
}
},
"value": null
},
At this point Im so confused I dont know where to look.
Heres my code now :
//App.js
import React from 'react';
import { jsonServerRestClient, Admin, Resource } from 'admin-on-rest';
import { PostList } from './Posts.js';
const App = () => (
<Admin restClient={jsonServerRestClient('http://127.0.0.1:5984/myproject/')}>
<Resource name="_design/getclients/_view/getclient" list={PostList} />
</Admin>
);
export default App;
And
//Posts.js
export const PostList = (props) => (
<List {...props}>
<Datagrid>
<TextField source="status" />
<TextField source="rating" />
</Datagrid>
</List>
);
The page loads but a little pink box pops up at the bottom saying :
The X-Total-Count header is missing in the HTTP Response. The jsonServer REST client expects responses
The RestClient is a bit of a murky beast. Not perfectly documented for sure.
But it is in the end quite straightforward if you know how the whole thing works together.
1) Admin-On-Rest has defined some REST types (below). These are usually shot off by Redux actions (in their meta tag). The system scans for these rest types and if it sees them, then it calls the RestClient
GET_LIST
GET_ONE
CREATE
UPDATE
DELETE
GET_MANY
GET_MANY_REFERENCE
The REST client is called with these types and some other params. It is the job of the rest client to interpret the type and then use the params to make a request to your API. For this AOR uses the new Fetch API that is built into browsers.
You can access it by calling. You should also go into AOR source code and check out how it works.
import { fetchUtils } from 'admin-on-rest';
2) The X total count is a header field that AOR needs for all responses to the GET_LIST type.
You can set this quite simply in your API. I use loopback and I set the X-Total-Count manually in a remote hook (don't worry about it if you don't know it)
It seems your api is still using the JSON server. JSON server is a dummy API. So your app is not connected to your couchDB right now.
https://github.com/typicode/json-server
If you are not using an api server like express or loopback, then you can also configure your restClient do all request and response handling. You have to construct the URL. Read the below link so you can follow my example code further down.
https://marmelab.com/admin-on-rest/RestClients.html#decorating-your-rest-client-example-of-file-upload
so something like this.
if (type === 'GET_LIST' && resource === 'posts') {
const url = http://127.0.0.1:5984/myproject/_design/getclients/_view/getclient/
options.method = 'GET';
return fetchUtils.fetchJson(url, options)
.then((response) => {
const {headers, json} = response;
//admin on rest needs the {data} key
return {data: json,
total: parseInt(headers.get('x-total-count').split('/').pop(), 10)}
})
You can also write a function like this to handle the request and response.
function handleRequestAndResponse(url, options={}) {
return fetchUtils.fetchJson(url, options)
.then((response) => {
const {headers, json} = response;
//admin on rest needs the {data} key
const data = {data: json}
if (headers.get('x-total-count')) {
data.total = parseInt(headers.get('x-total-count').split('/').pop(), 10)
} else {
data.total = json.length // this is why the X-Total-Count is needed by Aor
}
}
}
// handle get_list responses
return {data: json,
total: } else {
return data
}
})
}
The above code has been formatted in the window and so might not work straight out of the box. But I hope you get the idea.

Resources