Skip to content

Commit 80ada83

Browse files
committed
Merge branch '2.3'
2 parents fadb5de + 7371446 commit 80ada83

20 files changed

+562
-82
lines changed

admin/getting-started.md

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,3 +228,170 @@ export default class extends Component {
228228
}
229229
}
230230
```
231+
232+
### Using the Hydra Data Provider Directly with react-admin
233+
234+
By default, the `HydraAdmin` component shipped with API Platform Admin will generate a convenient admin interface for every resources and every properties exposed by the API. But sometimes, you may prefer having full control over the generated admin.
235+
236+
To do so, an alternative approach is [to configure every react-admin components manually](https://marmelab.com/react-admin/Tutorial.html) instead of letting the library generating it, but to still leverage the built-in Hydra [data provider](https://marmelab.com/react-admin/DataProviders.html):
237+
238+
```javascript
239+
// admin/src/App.js
240+
241+
import React, { Component } from 'react';
242+
import { Admin, Resource } from 'react-admin';
243+
import parseHydraDocumentation from '@api-platform/api-doc-parser/lib/hydra/parseHydraDocumentation';
244+
import { hydraClient, fetchHydra as baseFetchHydra } from '@api-platform/admin';
245+
import authProvider from './authProvider';
246+
import { Redirect } from 'react-router-dom';
247+
import { createMuiTheme } from '@material-ui/core/styles';
248+
import Layout from './Component/Layout';
249+
import { UserShow } from './Components/User/Show';
250+
import { UserEdit } from './Components/User/Edit';
251+
import { UserCreate } from './Components/User/Create';
252+
import { UserList } from './Components/User/List';
253+
254+
const theme = createMuiTheme({
255+
palette: {
256+
type: 'light'
257+
},
258+
});
259+
260+
const entrypoint = process.env.REACT_APP_API_ENTRYPOINT;
261+
const fetchHeaders = {'Authorization': `Bearer ${window.localStorage.getItem('token')}`};
262+
const fetchHydra = (url, options = {}) => baseFetchHydra(url, {
263+
...options,
264+
headers: new Headers(fetchHeaders),
265+
});
266+
const dataProvider = api => hydraClient(api, fetchHydra);
267+
const apiDocumentationParser = entrypoint => parseHydraDocumentation(entrypoint, { headers: new Headers(fetchHeaders) })
268+
.then(
269+
({ api }) => ({api}),
270+
(result) => {
271+
switch (result.status) {
272+
case 401:
273+
return Promise.resolve({
274+
api: result.api,
275+
customRoutes: [{
276+
props: {
277+
path: '/',
278+
render: () => <Redirect to={`/login`}/>,
279+
},
280+
}],
281+
});
282+
283+
default:
284+
return Promise.reject(result);
285+
}
286+
},
287+
);
288+
289+
export default class extends Component {
290+
state = { api: null };
291+
292+
componentDidMount() {
293+
apiDocumentationParser(entrypoint).then(({ api }) => {
294+
this.setState({ api });
295+
}).catch((e) => {
296+
console.log(e);
297+
});
298+
}
299+
300+
render() {
301+
if (null === this.state.api) return <div>Loading...</div>;
302+
return (
303+
<Admin api={ this.state.api }
304+
apiDocumentationParser={ apiDocumentationParser }
305+
dataProvider= { dataProvider(this.state.api) }
306+
theme={ theme }
307+
appLayout={ Layout }
308+
authProvider={ authProvider }
309+
>
310+
<Resource name="users" list={ UserList } create={ UserCreate } show={ UserShow } edit={ UserEdit } title="Users"/>
311+
</Admin>
312+
)
313+
}
314+
}
315+
```
316+
317+
And accordingly create files `Show.js`, `Create.js`, `List.js`, `Edit.js`
318+
in the `admin/src/Component/User` directory:
319+
320+
```javascript
321+
// admin/src/Component/User/Create.js
322+
323+
import React from 'react';
324+
import { Create, SimpleForm, TextInput, email, required } from 'react-admin';
325+
326+
export const UserCreate = (props) => (
327+
<Create { ...props }>
328+
<SimpleForm>
329+
<TextInput source="email" label="Email" validate={ email() } />
330+
<TextInput source="plainPassword" label="Password" validate={ required() } />
331+
<TextInput source="name" label="Name"/>
332+
<TextInput source="phone" label="Phone"/>
333+
</SimpleForm>
334+
</Create>
335+
);
336+
337+
```
338+
339+
```javascript
340+
// admin/src/Component/User/Edit.js
341+
342+
import React from 'react';
343+
import { Edit, SimpleForm, DisabledInput, TextInput, DateInput, email } from 'react-admin';
344+
345+
export const UserEdit = (props) => (
346+
<Edit {...props}>
347+
<SimpleForm>
348+
<DisabledInput source="originId" label="ID"/>
349+
<TextInput source="email" label="Email" validate={ email() } />
350+
<TextInput source="name" label="Name"/>
351+
<TextInput source="phone" label="Phone"/>
352+
<DateInput disabled source="createdAt" label="Date"/>
353+
</SimpleForm>
354+
</Edit>
355+
);
356+
```
357+
358+
```javascript
359+
// admin/src/Component/User/List.js
360+
361+
import React from 'react';
362+
import { List, Datagrid, TextField, EmailField, DateField, ShowButton, EditButton } from 'react-admin';
363+
import { CustomPagination } from '../Pagination/CustomPagination';
364+
365+
export const UserList = (props) => (
366+
<List {...props} title="Users" pagination={ <CustomPagination/> } perPage={ 30 }>
367+
<Datagrid>
368+
<TextField source="originId" label="ID"/>
369+
<EmailField source="email" label="Email" />
370+
<TextField source="name" label="Name"/>
371+
<TextField source="phone" label="Phone"/>
372+
<DateField source="createdAt" label="Date"/>
373+
<ShowButton />
374+
<EditButton />
375+
</Datagrid>
376+
</List>
377+
);
378+
```
379+
380+
```javascript
381+
// admin/src/Component/User/Show.js
382+
import React from 'react';
383+
import { Show, SimpleShowLayout, TextField, DateField, EmailField, EditButton } from 'react-admin';
384+
385+
export const UserShow = (props) => (
386+
<Show { ...props }>
387+
<SimpleShowLayout>
388+
<TextField source="originId" label="ID"/>
389+
<EmailField source="email" label="Email" />
390+
<TextField source="name" label="Name"/>
391+
<TextField source="phone" label="Phone"/>
392+
<DateField source="createdAt" label="Date"/>
393+
<EditButton />
394+
</SimpleShowLayout>
395+
</Show>
396+
);
397+
```

client-generator/images/demo.gif

3.67 MB
Loading
320 KB
Loading
255 KB
Loading
Loading
524 KB
Loading
263 KB
Loading

client-generator/index.md

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,32 @@
11
# The API Platform Client Generator
22

3-
API Platform Client Generator is a generator for scaffolding apps with Create-Retrieve-Update-Delete features for any API exposing a Hydra documentation. Currently the following targets are available:
3+
Client Generator is the fastest way to scaffold fully-featured webapps and native mobile apps from APIs supporting the [Hydra](http://www.hydra-cg.com/) format.
44

5-
* React/Redux
6-
* React Native
7-
* Vue.js
5+
![Screencast](images/demo.gif)
86

9-
The generator works especially well with APIs built with the [API Platform](https://api-platform.com) framework.
7+
*Generated React and React Native apps, updated in real time*
8+
9+
It is able to generate apps using the following frontend stacks:
10+
11+
* [React with Redux](react.md)
12+
* [React Native](react-native.md)
13+
* [Vue.js](vuejs.md)
14+
15+
Client Generator works especially well with APIs built with the [API Platform](https://api-platform.com) framework.
1016

1117
## Features
1218

13-
* Generate high-quality ES6 components and files built with [React](https://facebook.github.io/react/), [Redux](http://redux.js.org), [React Router](https://reacttraining.com/react-router/) and [Redux Form](http://redux-form.com/) including:
14-
* A list view
15-
* A creation form
16-
* An editing form
17-
* A delete button
18-
* Use the Hydra API documentation to generate the code
19-
* Generate the suitable HTML5 input type (`number`, `date`...) according to the type of the API property
20-
* Display of the server-side validation errors under the related input (if using API Platform Core)
21-
* Client-side validation (`required` attributes)
22-
* The generated HTML is compatible with [Bootstrap](https://getbootstrap.com/) and includes mandatory classes
23-
* The generated HTML code is accessible to people with disabilities ([ARIA](https://www.w3.org/WAI/intro/aria) support)
24-
* The Redux and the React Router configuration is also generated
19+
* Generate high-quality ES6:
20+
* list view (with pagination)
21+
* detail view
22+
* creation form
23+
* edition form
24+
* delete button
25+
* Supports to-one and to-many relations
26+
* Uses the appropriate input type (`number`, `date`...)
27+
* Client-side validation
28+
* Subscribe to data updates pushed by servers supporting [the Mercure protocol](https://mercure.rocks)
29+
* Display server-side validation errors under the related input (if using API Platform Core)
30+
* Integration with [Bootstrap](https://getbootstrap.com/) and [FontAwesome](https://fontawesome.com/) (Progressive Web Apps)
31+
* Integration with [React Native Elements](https://react-native-training.github.io/react-native-elements/)
32+
* Accessible to people with disabilities ([ARIA](https://www.w3.org/WAI/intro/aria) support in webapps)

client-generator/react-native.md

Lines changed: 43 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,44 @@
1-
React Native generator
2-
======================
1+
# React Native generator
32

4-
Create a React Native application using [React Community's Create React Native App](https://github.com/react-community/create-react-native-app)
3+
![List](images/list.png)
4+
5+
## Install
6+
7+
To use this generator you need [Node.js](https://nodejs.org/) and [Yarn](https://yarnpkg.com/) (or [NPM](https://www.npmjs.com/)) installed.
8+
To run the command line tool, we also recommend using [npx](https://www.npmjs.com/package/npx).
9+
10+
Create a React Native application using [Expo CLI](https://docs.expo.io/versions/latest/workflow/expo-cli).
511

612
```bash
7-
$ yarn add -g expo-cli
13+
$ yarn global add expo-cli
814
$ expo init my-app
15+
# When asked, choose to use the blank template
916
$ cd my-app
1017
```
1118

12-
Install: React Native Elements, React Native Router Flux, React Native Vector Icons, Redux, React Redux, Redux Thunk, Redux Form, Prop Types
19+
Install the required dependencies:
1320

1421
```bash
15-
$ yarn add redux react-redux redux-thunk redux-form react-native-elements react-native-router-flux
16-
react-native-vector-icons prop-types
22+
$ yarn add redux react-redux redux-thunk redux-form react-native-elements react-native-router-flux react-native-vector-icons prop-types whatwg-url buffer react-native-event-source
1723
```
1824

19-
Install the generator globally:
25+
## Generating a Native App
26+
27+
In the app directory, generate the files for the resource you want:
2028

2129
```bash
22-
$ yarn global add @api-platform/client-generator
30+
$ npx @api-platform/client-generator https://demo.api-platform.com . -g react-native --resource book
2331
```
2432

25-
In the app directory, generate the files for the resource you want:
33+
Replace the URL with the entrypoint of your Hydra-enabled API.
34+
Omit the resource flag to generate files for all resource types exposed by the API.
2635

27-
```
28-
$ generate-api-platform-client https://demo.api-platform.com -g react-native --resource foo
29-
# Replace the URL by the entrypoint of your Hydra-enabled API
30-
# Omit the resource flag to generate files for all resource types exposed by the API
31-
```
32-
Create **Router.js** file to import all routes
36+
Create a `Router.js` file to import all routes:
3337

3438
```javascript
3539
import React from 'react';
3640
import { Router, Stack } from 'react-native-router-flux';
37-
//replace "book" with the name of resource type
41+
// Replace "book" with the name of the resource type
3842
import BookRoutes from './routes/book';
3943

4044
const RouterComponent = () => {
@@ -49,16 +53,31 @@ const RouterComponent = () => {
4953

5054
export default RouterComponent;
5155
```
52-
Here is an example of **App.js**
56+
57+
Here is an example of an `App.js` file:
5358

5459
```javascript
5560
import React, { Component } from 'react';
5661
import { Provider } from 'react-redux';
5762
import thunk from 'redux-thunk';
58-
import { createStore, applyMiddleware, combineReducers} from 'redux';
63+
import { createStore, applyMiddleware, combineReducers } from 'redux';
5964
import { View } from 'react-native';
6065
import {reducer as form} from 'redux-form';
61-
//replace "book" with the name of resource type
66+
67+
// see https://github.com/facebook/react-native/issues/14796
68+
import { Buffer } from 'buffer';
69+
global.Buffer = Buffer;
70+
71+
// see https://github.com/facebook/react-native/issues/16434
72+
import { URL, URLSearchParams } from 'whatwg-url';
73+
global.URL = URL;
74+
global.URLSearchParams = URLSearchParams;
75+
76+
// see https://github.com/facebook/react-native/issues/12890
77+
import RNEventSource from 'react-native-event-source';
78+
global.EventSource = RNEventSource;
79+
80+
// Replace "book" with the name of resource type
6281
import book from './reducers/book';
6382
import Router from './Router';
6483

@@ -80,11 +99,12 @@ export default class App extends Component {
8099
```
81100

82101
The code is ready to be executed!
102+
83103
```bash
84104
$ expo start
85105
```
86106

87-
#### Example of running application on IOS simulator
107+
## Screenshots in iOS Simulator
88108

89-
![List](images/list.png) ![Show](images/item.png)
90-
![Add](images/adnew.png) ![Delete](images/del.png)
109+
![List](images/react-native/list.png) ![Show](images/react-native/show.png)
110+
![Add](images/react-native/add.png) ![Delete](images/react-native/delete.png)

0 commit comments

Comments
 (0)