Edit without passing id as identifier - admin-on-rest

Suppose I have a List and and Edit of items of the following schema:
STRING key UNIQUE PRIMARY
STRING value
The problem is that when using admin-on-rest, the Edit uses the id by default to generate the request, if it does not have one, it passes undefined. How can I change that on the SimpleForm so it uses another parameter instead of id - in my case, key -.
Example of the List and Edit:
export const ParamList = props => (
<List title = "All params" { ...props} >
<Datagrid >
<TextField source = "key" sortable={false} />
<TextField source = "value" sortable={false} />
<EditButton />
</Datagrid>
</List>
);
export const ParamEdit = props => (
<Edit title = {< ParamTitle />} { ...props } >
<SimpleForm >
<TextInput source = "key" />
<TextInput source = "value" />
</SimpleForm>
</Edit >
);
When I send the editing, the following request is made:
PUT www.randomurl.com/param/undefined
But i wished it was something like:
PUT www.randomurl.com/param/<item's key>
I have seen that on the documentation that is possible to set customize the requests as a whole. But was thinking if there is something easier, like adding a parameter on SimpleForm:
export const ParamEdit = props => (
<Edit title = {< ParamTitle />} { ...props } >
<SimpleForm id={"key"}>
<TextInput source = "key" />
<TextInput source = "value" />
</SimpleForm>
</Edit >
);
Thanks in advance.

As explained in the documentation, the correct and only way to do that is to write a custom restClient. See https://marmelab.com/admin-on-rest/FAQ.html#can-i-have-custom-identifiersprimary-keys-for-my-resources

Related

How to change value of input in Admin on Rest from another component in Create form?

I've reduced this to a very simple case for ease of discussion. I have a simple create form with 1 field and 1 button. I would like the button to set the value of the TextInput to "Hello" without submitting the form. How is this possible in admin on rest? eg:
export const TestCreate = (props) => (
<Create title={<TestTitle />} {...props}>
<SimpleForm>
<TextInput source="title" />
<TitleSetterButton />
</SimpleForm>
</Create>
);
Been struggling with this for a while - it should be simple so hopefully there's an easy answer.
I was able to setup a Sample form using their example application
// in src/posts.js
import React from 'react';
import { List, Edit, Create, Datagrid, ReferenceField, TextField, EditButton, DisabledInput, LongTextInput, ReferenceInput, required, SelectInput, SimpleForm, TextInput } from 'admin-on-rest';
import FlatButton from 'material-ui/FlatButton';
export const PostList = (props) => (
<List {...props}>
<Datagrid>
<TextField source="id" />
<ReferenceField label="User" source="userId" reference="users">
<TextField source="name" />
</ReferenceField>
<TextField source="title" />
<TextField source="body" />
<EditButton />
</Datagrid>
</List>
);
const PostTitle = ({ record }) => {
return <span>Post {record ? `"${record.title}"` : ''}</span>;
};
export class Testing extends React.Component {
render() {
return <input type="text" />
}
}
export class PostCreate extends React.Component {
componentDidMount() {
console.log(this)
}
constructor(props) {
super(props);
this.handleCustomClick = this.handleCustomClick.bind(this);
// this.fieldOptions = this.fieldOptions.bind(this);
}
handleCustomClick() {
this.fields.title.handleInputBlur("tarun lalwani");
this.fields.body.handleInputBlur("this is how you change it!");
}
render () {
let refOptions = {ref: (e) => {
if (e && e.constructor && e.props && e.props.name) {
this.fields = this.fields || {};
this.fields[e.props.name] = e;
}
}}
return (
<Edit title={<PostTitle />} {...this.props}>
<SimpleForm>
<DisabledInput source="id" />
<ReferenceInput label="User" source="userId" reference="users" validate={required}>
<SelectInput optionText="name" />
</ReferenceInput>
<TextInput source="title" options={refOptions}/>
<LongTextInput source="body" options={refOptions}/>
<FlatButton primary label="Set Value" onClick={this.handleCustomClick} />
</SimpleForm>
</Edit>
);
}
}
Before click of the button
After clicking Set Value
And then after clicking Save you can see the actual changed values get posted

How to solve this issue with redux form? It shows syntax error

We are going to use redux form for all our app's forms. The app may have 5 different forms for different purposes. The trouble with this first form is that it shows syntax error. But before implementing redux form it was worked with the same syntax. What is the problem with this code?
import React, {Component} from "react";
import {Text, Image, KeyboardAvoidingView, Platform,View} from "react-native";
import {
Content,
Form,
Item,
Input,
Icon,
Button,
ListItem,
Row,
Col,
Grid,
Toast,
Container,
Left,
Right,
Body
} from "native-base";
import styles from "../styles/formstyle";
import { Field, reduxForm } from "redux-form";
const required = value => (value ? undefined : "Required");
const maxLength = max => value => (value && value.length > max ? `Must be ${max} characters or less` : undefined);
const maxLength15 = maxLength(15);
const minLength = min => value => (value && value.length < min ? `Must be ${min} characters or more` : undefined);
const minLength8 = minLength(8);
const email = value =>
value && !/^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(value) ? "Invalid email address" : undefined;
const alphaNumeric = value => (value && /[^a-zA-Z0-9 ]/i.test(value) ? "Only alphanumeric characters" : undefined);
class SigninScreen extends Component {
static navigationOptions = {
gesturesEnabled: false,
};
renderInput({ input, label, type, meta: { touched, error, warning } }) {
return (
<Item style={styles.item}
error={error && touched}
>
<Icon
active
name="mail"
style={styles.icon}
/>
<Input
{...input.name="email"}
ref={c => (this.textInput = c)}
placeholder="Email"
placeholderTextColor="#a4916d"
style={styles.input}
/>
</Item>
<Item style={styles.item} error={error && touched}>
<Icon
active
name="lock"
style={styles.icon}
/>
<Input
{...input.name="password"}
ref={c => (this.textInput = c)}
secureTextEntry={true}
placeholder="Password"
placeholderTextColor="#a4916d"
style={styles.input}
/>
</Item>
);
}
login() {
if (this.props.valid) {
this.props.navigation.navigate("Drawer");
} else {
Toast.show({
text: "Enter Valid Username & password!",
duration: 2000,
position: "top",
textStyle: { textAlign: "center" },
});
}
}
render() {
return (
<Image style={background.img} source={require("../img/cover.jpg")}>
<Container style={styles.content}>
<Form>
<Field name="email"
validate={[email, required]} />
<Field
name="password"
component={this.renderInput}
validate={[alphaNumeric, minLength8, maxLength15, required]}
/>
</Form>
<ListItem
style={styles.list}
>
<Left>
<Button
primary
full
style={{width:"90%"}}
onPress={() => this.props.navigation.navigate("Signup")}
>
<Text style={{color: "#0dc49d"}}>fb</Text>
</Button>
</Left>
<Body/>
<Right>
<Button
danger
full
onPress={() =>
this.props.navigation.navigate("Forgetpass")}
>
<Text style={{color: "#0dc49d"}}>google</Text>
</Button>
</Right>
</ListItem>
<Button
full
style={{backgroundColor: "#0dc49d", width:"90%"}}
onPress={() => this.login()}
>
<Text style={{color:"#ffffff"}}>Sign In</Text>
</Button>
</Container>
</Image>
);
}
}
export default reduxForm({
form: 'test'
})(SigninScreen)
It says JSX elements must be wrapped an enclosing tag. Eslint shows the second component inside the renderinput. I am using it with native-base. What can cause this error? Also, can you check please if the communication within renderinput and fields component is right? I am not sure that after solving the syntax error this code will work :(
Thanks in advance!
renderInput method is returning two different items. You need to wrap them into a single wrapped component like the error is saying.
Example
renderInput({ input, label, type, meta: { touched, error, warning } }) {
return (
<View>
<Item style={styles.item} error={error && touched}>
<Icon active name="mail" style={styles.icon} />
<Input {...input.name="email"} ref={c => (this.textInput = c)} placeholder="Email" placeholderTextColor="#a4916d" style={styles.input} />
</Item>
<Item style={styles.item} error={error && touched}>
<Icon active name="lock" style={styles.icon} />
<Input {...input.name="password"} ref={c => (this.textInput = c)} secureTextEntry={true} placeholder="Password" placeholderTextColor="#a4916d" style={styles.input} />
</Item>
</View>
);
}

Independent "Submit" button for tabbed form in Create

I already made 2 types of create page:
Create single record.
Import multiple records from xlsx file.
Now I want to implement 2 independent buttons:
Save
Import
meaning that when I click on button 1, only button 1 works.
Here is my code:
<Create {...this.props}>
<TabbedForm toolbar="">
<FormTab label="Single record">
<ReferenceInput label="Centre" source="centre" reference="centre" sort={{ field: 'name', order: 'ASC' }} allowEmpty>
<SelectInput optionText="name" />
</ReferenceInput>
<TextInput source="fullname" />
<TextInput source="serial " />
<TextInput source="birthday" />
<TextInput source="join_date" />
<TextInput source="remark" />
<SaveButton label="Save" redirect="show" submitOnEnter={true} />
</FormTab>
<FormTab label="Import from xlsx">
<ReferenceInput label="Centre" source="centre_import" reference="centre" sort={{ field: 'name', order: 'ASC' }} allowEmpty>
<SelectInput optionText="name" />
</ReferenceInput>
<label id="customLabel">
<input id="upload" ref={(input) => { this.textInput = input; }} type="file" hidden
onClick={(event)=> {
event.target.value = null;
}}
onChange={
(event) => {
this.fileName.textContent = event.target.files[0].name;
}
}
/>
<FlatButton primary label="Select file" icon={<ActionFile />} onClick={() => {
this.textInput.click();
}}/>
<span id="fileName" ref={(span) => { this.fileName = span; }}></span>
</label>
<SaveButton label="Import" redirect={false} submitOnEnter={true} />
</FormTab>
</TabbedForm>
</Create>
The easiest way would be to keep a single button here. You may add a text inside the importation tab explaining that clicking on save will import the file.
However, you still have to deal with the redirection. To do so, you'll have to implement a custom SaveButton:
Copy the code of the default SaveButton into a SaveOrImportButton file.
Update its mapStateToProps function and use redux-form getFormValues selector to inspect the form values and determine whether its an importation.
Use this knowledge to customize the button:
You may update the label to Import if the user selected a file. The label will update immediately after the file field gets dirty.
You can change the redirect value at L22.
Use this button inside a Toolbar component and pass this component to the toolbar prop of the Create component.

How can I create a SaveButton which returns to me to a special URL

I know that I can use an url as redirect parameter for an SaveButton like this:
const ResourcePropertyEditToolbar = props => <Toolbar {...props} >
<SaveButton />
<SaveButton label="save and resource" redirect={"/resource/21"}
submitOnEnter={false} raised={false} />
</Toolbar>;
As you see the "/resource/21" is hard coded and it works (for this entry ;-) How can I dynamically create the url? The value itself is included in the current data set which is edited, like this:
export const ResourcePropertyEdit = (props) => (
<Edit {...props} >
<SimpleForm toolbar={<ResourcePropertyEditToolbar />}>
<NumberInput source="position" defaultValue="1"/>
<TextInput source="property_name" validate={required}/>
<ReferenceInput label="Resource" source="fk_resource"
reference="resource">
<AutocompleteInput optionText="shortname"/>
</ReferenceInput>
</SimpleForm>
</Edit>
);
It is the value of the selected fk_resource.
Here is the code snippet for the base Form with the ReferenceManyField Entry, the target is to jump back to this view after editing a resource_property.
export const ResourceEdit = (props) => (
<Edit {...props}>
<SimpleForm>
<TextInput source="shortname" validate={required}/>
<ReferenceManyField target="fk_resource"
reference="resource_property" >
<Datagrid>
<NumberField source="position"/>
<TextField source="property_name"/>
<ReferenceField label="Property" source="fk_property" reference="property" allowEmpty>
<TextField source="shortname"/>
</ReferenceField>
<EditButton/>
</Datagrid>
</ReferenceManyField>
</SimpleForm>
</Edit>
);
Thanks for any help
The current data set is called the record in AOR. If the URL is in the record you can access it simply by
const ResourcePropertyEditToolbar = props => <Toolbar {...props} >
<SaveButton />
<SaveButton label="save and resource" redirect={props.record.url ? props.record.url : null}
submitOnEnter={false} raised={false} />
</Toolbar>

How can I configure <List> title from a config file in admin-on-rest

I need to customize the list title, but to allow it in diff. languages.
I try:
<List title='Hi there' ...>
and title is changed as expected BUT how do I make the string configurable from a file?
<List title={translate('myroot.list.header') ...>
doesn't work...
Try with a custom title component:
import { translate } from 'admin-on-rest';
const ListTitle = translate(({ translate }) => <span>{translate('my.title')}</span>);
<List title={<ListTitle />} >
hope that it will help you:
customMessages.js
export const customMessages = {
titles: {
one: 'First Title',
two: 'Second Title',
},
}
App.js
import { englishMessages } from 'admin-on-rest';
import { customMessages } from './customMessages';
const messages = {
'en': {...englishMessages, ...customMessages},
};
resources.js
export const ResourceList = (props) => (
<List {...props} title="titles.one">
<Datagrid>
<TextField source="id" />
<TextField source="text" />
<EditButton />
<DeleteButton />
</Datagrid>
</List>
);
Replace next to your names:
resources.js
ResourceList

Resources