admin-on-rest 0.8 is out
Time for our monthly release of admin-on-rest! Today, we're announcing version 0.8. It features three major improvements:
Besides, this version focuses on removing the need to write a custom app for advanced usage. Most common customizations (custom layout, reducers, sagas, etc) are now handled by default.
Read on to see all that in detail.
Translation
If your admin users speak different languages, you're in luck: you can now offer a multilingual interface. Admin-on-rest relies on Polyglot.js (Airbnb's JS translation library) for that. It just boils down to providing a dictionary file, called messages
.
Here is how you can provide both English and French, and let admin-on-rest choose the right language according to the browser locale:
import React from "react";
import {
Admin,
Resource,
englishMessages,
resolveBrowserLocale,
} from "admin-on-rest";
import frenchMessages from "aor-language-french";
const messages = {
fr: frenchMessages,
en: englishMessages,
};
const App = () => (
<Admin locale={resolveBrowserLocale()} messages={messages}>
...
</Admin>
);
export default App;
The blog example uses this technique ; here is it in English and French:
Interface translation in admin-on-rest supports custom labels for resources and fields, provides a translation helper for your own components, supports smart pluralization, and allows for language switch at runtime. To learn more, head to the translation documentation.
Authentication
It's hard to design a generic authentication system for an app like admin-on-rest, because there are so many possible strategies. We decided to delegate the actual authentication to a simple function, similar to the restClient
. It's called the authClient
, and it allows to implement any authentication strategy you need: Basic Auth, OAuth, JWT, social login, etc.
Admin-on-rest provides the login form and logout button. What they do depends on your implementation of the authClient
. For instance, to query an authentication route via HTTPS and store the credentials (a token) in local storage, configure authClient
as follows:
// in src/authClient.js
import { AUTH_LOGIN } from "admin-on-rest";
export default (type, params) => {
if (type === AUTH_LOGIN) {
const { username, password } = params;
const request = new Request("https://mydomain.com/authenticate", {
method: "POST",
body: JSON.stringify({ username, password }),
headers: new Headers({ "Content-Type": "application/json" }),
});
return fetch(request)
.then(response => {
if (response.status < 200 || response.status >= 300) {
throw new Error(response.statusText);
}
return response.json();
})
.then(({ token }) => {
localStorage.setItem("token", token);
});
}
return Promise.resolve();
};
Then, pass this client to the <Admin>
component:
// in src/App.js
import authClient from "./authClient";
const App = () => <Admin authClient={authClient}>...</Admin>;
Now the app requires authentication:
To use the credentials when calling REST API routes, you have to tweak, this time, the restClient
. As explained in the REST client documentation, simpleRestClient
and jsonServerRestClient
take an httpClient
as second parameter. That's the place where you can change request headers, cookies, etc.
For instance, to pass the token obtained during login as an Authorization
header, configure the REST client as follows:
import { simpleRestClient, fetchUtils, Admin, Resource } from "admin-on-rest";
const httpClient = (url, options) => {
if (!options.headers) {
options.headers = new Headers({ Accept: "application/json" });
}
const token = localStorage.getItem("token");
options.headers.set("Authorization", `Bearer ${token}`);
return fetchUtils.fetchJson(url, options);
};
const restClient = simpleRestClient("http://localhost:3000", httpClient);
const App = () => (
<Admin restClient={restClient} authClient={authClient}>
...
</Admin>
);
Now, every request to the REST backend will include an Authorization
header with the token obtained at login.
You can see how this can be adapted to any authentication strategy. For details, refer to the Authentication documentation.
<ImageInput>
Upload images from within your admin with this new component:
import { ImageInput, ImageField } from "admin-on-rest/lib/mui";
<ImageInput source="pictures" label="Related pictures" accept="image/*">
<ImageField source="src" title="title" />
</ImageInput>;
The actual upload logic is delegated to the restClient
. So whether you're base64 encoding the image to send it in JSON, or using multipart form, you're covered! See the details in the <ImageInput>
documentation.
Other Notable Additions
The Changelog lists more changes:
- Add
<CheckboxGroupInput>
component - Add the ability to hook up custom sagas in
<Admin>
- Add the ability to hook up custom reducers in
<Admin>
- Add
filter
prop to<List>
component for permanent filters - Add support for
defaultValue
in filters - Add support for functions as value for the
defaultValue
prop - Add ability to disable sorting on datagrid headers
- Add
perPage
,sort
, andfilter
props to<ReferenceManyField>
- Add
label
prop to all buttons - Add Custom Actions documentation
- Add CSS prefix to flex-box elements (yshing)
- Improve Delete button labels
- Update optimistic rendering to also work with custom fetch actions
- Speed up
<Datagrid>
rendering - Refactor response side effects to a normal saga
- Upgrade
redux-saga
to v0.14.2 - Fix disconnection on dashboard
- Fix edge case where list filter isn't shown
- Fix validation for
<AutocompleteInput>
,<ReferenceInput>
, and<SelectInput>
(AkselsLedins)
Some of these changes add capabilities to the <Admin>
component, and remove the need for a custom app in most common cases.
What doesn't appear in the changelog is the work on the documentation: each new feature is documented, with an example use case, and in most cases, a screenshot. We've also added new chapters, and an index. Make sure you browse the updated documentation, even if you already read it once.
Backward Incompatible Changes
Version 0.8 introduces two breaking changes:
- Rename
defaultSort
prop tosort
in<List>
component, to make it consistent with the props of<ReferenceInput>
and<ReferenceManyField>
- Rename
filter
props (the one accepting a<Filter>
element) tofilters
in<List>
component
Considering the amount of changes in this version, this is pretty encouraging. It means the current architecture already allows to build sophisticated admins. In other terms, we're nearing a stable API.
What's Next
Version 1.0!
Version 0.8 is already feature complete, i.e. it is enough to build a demo equivalent to ng-admin-demo. Stay tuned for the announcement of admin-on-rest-demo!
Then, why didn't we call it 1.0? Because it's a bit too soon. It took us a month of hectic work to develop version 0.8, and we need a bit more time to iron out the code, and fix a few corner cases. "A bit more times" means a month, since we release one new version every month. That means version 1.0, the official stable released, is scheduled for mid-March.
We also hope to upgrade two major dependencies, react-router
and material-ui
, who announce backwards incompatible changes. The switch from 0.8 to 1.0 will therefore probably need a manual upgrade, too.
Please send upgrade to 0.8 and send us your feedback about this version in the GitHub repository. And if you like it, share the word! Admin-on-rest gains in popularity with each release.