Airtable

Airtable

2019/05/06 (增加範例程式)

2019/07/22 (增補資料)

Airtable

Airtable是個雲端服務,提供類似試算表的資料庫,可以利用airtable提供的javascript library或者利用REST存取資料。在這邊先介紹如何透過REST存取資料。

Axios

react可以利用javascript內建的fetch來呼叫RESTful web services,也可以利用Axios,採用Axios的好處是,Axios會自動處理json的內容 (詳參: Fetch vs. Axios.js for making http requests)。

首先,先安裝axios

npm install axios

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import ReactApp from './components/react-app';
import * as serviceWorker from './serviceWorker';

ReactDOM.render(<ReactApp />, document.getElementById('root'));

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();

連接airtable,是透過Bearer token,所以,要設定header,並將airtable的API取代程式中的XXXXXXXXXXXXXXXXX。

Authorization: 'Bearer XXXXXXXXXXXXXXXXX'

也要將appLNbQOB0WL1ZeDD取代為你的Base id,Table%201,取代為你的table名稱,如果table名稱有空白,就要用%20取代(URI Encode)。

    constructor(props) {
        super(props);
        this.state = {students: []};
        this.Axios = axios.create({
            baseURL: "https://api.airtable.com/v0/appLNbQOB0WL1ZeDD/Table%201?view=Grid%20view",
            headers: {'Authorization': 'Bearer XXXXXXXXXXXXXXXXX'}
        });
    }

取得的資料格式:

{
    "records": [
        {
            "id": "rec6USTCW7WQS1D4H",
            "fields": {
                "City": "台北市",
                "Age": 8,
                "Name": "Ben"
            },
            "createdTime": "2019-03-08T06:18:45.000Z"
        },
        {
            "id": "recM3qifn7g8hlLhy",
            "fields": {
                "City": "新北市",
                "Age": 6,
                "Name": "Ana"
            },
            "createdTime": "2019-03-08T06:18:45.000Z"
        },
        {
            "id": "rec9yMWV1aqzQv0g3",
            "fields": {
                "City": "台北市",
                "Age": 7,
                "Name": "Mary"
            },
            "createdTime": "2019-03-08T06:18:45.000Z"
        }
    ],
    "offset": "rec9yMWV1aqzQv0g3"
}

因為資料會包在records裡,所以,取得資料時,要多records

    componentDidMount() {
        let _this = this;
        this.Axios.get()
          .then(function (response) {
             console.log(response);
            _this.setState({students: response.data.records});
          })
          .catch(function (error) {
            console.log(error);
          });

components/react-app.js

import React from 'react';
import axios from 'axios';

import StudentList from './student-list'
 
export default class ReactApp extends React.Component {
 
    constructor(props) {
        super(props);
        this.state = {students: []};
        this.Axios = axios.create({
            baseURL: "https://api.airtable.com/v0/appLNbQOB0WL1ZeDD/Table%201?view=Grid%20view",
            headers: {'Authorization': 'Bearer XXXXXXXXXXXXXXXXX'}
        });
    }
 
    componentDidMount() {
        let _this = this;
        this.Axios.get()
          .then(function (response) {
             console.log(response);
            _this.setState({students: response.data.records});
          })
          .catch(function (error) {
            console.log(error);
          });
    }
 
    render() {
        return (
                <div>
                  <StudentList students={this.state.students}/>
                </div>
            )
    }
}

components/student-list.js

import React from 'react';
import Student from './student'
 
export default class StudentList extends React.Component{
     
    render() {
        var students = this.props.students.map((student, i) =>
            <Student key={i} student={student}/>
        );
         
        return (
            <table>
                <tbody>
                    <tr>
                        <th>姓名</th>
                        <th>年齡</th>
                        <th>城市</th>
                    </tr>
                    {students}
                </tbody>
            </table>
        )
    }
}

因為資料內容又包在fields中,取得時要加fields

student.js

import React from 'react';
 
export default class Student extends React.Component{
    render() {
        return (
            <tr>
                <td>{this.props.student.fields.Name}</td>
                <td>{this.props.student.fields.Age}</td>
                <td>{this.props.student.fields.City}</td>
            </tr>
        )
    }
}

import React from 'react';
import axios from 'axios';

export default class StudentAdd extends React.Component{

    handleChange=(event)=>{
        this.setState({ [event.target.name]: event.target.value });
    }
    handleSubmit=(event)=> {
        //event.preventDefault(); 
        //console.log("Clicked!");
        const student = {fields:{
          Name: this.state.name,
          Age: Number(this.state.age),
          City: this.state.city
        }
        };
        
        let axiosConfig = { headers: { Authorization: 'Bearer XXXXXXXX' ,
          'Content-Type': 'application/json' } } 
        api.post("https://api.airtable.com/v0/appLNbQOB0WL1ZeDD/Table%201?view=Grid%20view",  student, axiosConfig )
          .then(res => {
            //console.log(res);
            //console.log(res.data);
          })
          .catch(function (error) {
            //console.log(error);
          });
          
        
    }
    
    render() {
         
        return (
            <div>
            <form onSubmit={this.handleSubmit}>
            <label>
              Person Name:
              <input type="text" name="name" onChange={this.handleChange} />
            </label>
            <label>
              Age:
              <input type="text" name="age" onChange={this.handleChange} />
            </label>
            <label>
              City:
              <input type="text" name="city" onChange={this.handleChange} />
            </label>

            <button type="submit">Add</button>
          </form>
          
            </div>
        )
    }
}

Airtable package

利用airtable提供的功能,要先安裝airtable

npm install airtable

student-list.js

import React, {useState, useEffect} from 'react';
import Student from './student'
import { Paper, Table, TableBody, TableHead, TableRow, TableCell } from '@material-ui/core/';
import useStyles from './styles';

 
export default function StudentList() {
  const classes = useStyles();
  const Airtable = require('airtable');
  const base = new Airtable({apiKey: 'XXXXXXXXXXXXXXXXXX'}).base('appLNbQOB0WL1ZeDD');


  const  [students,setStudents] =  useState([]);
    useEffect(()=>{
        base('Table 1').select({view: "Grid view"}).all()
        .then(records => {
            setStudents(records);
        }).catch(err => {
            console.error(err);
        });

    },[])
 
           
    return (
        <Paper className={classes.root}>
        <Table className={classes.table}>
            <TableHead>
                <TableRow>
                    <TableCell>ID</TableCell>
                    <TableCell>Age</TableCell>
                    <TableCell>City</TableCell>
                </TableRow>
            </TableHead>
            <TableBody>

                {students.map((student, i) =>
                <Student key={i} student={student}/>)}
            </TableBody>
        </Table>
        </Paper>
    )
}


styles.js

import { makeStyles } from '@material-ui/core/styles';
const useStyles = makeStyles(theme => ({
    root: {
      //width: '85%',
      marginTop: theme.spacing(3),
      marginLeft: theme.spacing(3),
      marginRight: theme.spacing(3),
      overflowX: 'auto',
    },
    table: {
      minWidth: 650,
    },
  }));
export default useStyles;

student.js

import React from 'react';
import { TableRow, TableCell } from '@material-ui/core/';

 
export default function Student(props){
    return (
        
        <TableRow key={props.student.fields.Name}>
            <TableCell>{props.student.fields.Name}</TableCell>
            <TableCell>{props.student.fields.Age}</TableCell>
            <TableCell>{props.student.fields.City}</TableCell>
        </TableRow>
    )
    
}

student-add.js

import React,{useState} from 'react';
//import axios from 'axios';

import { Paper,TextField, Button } from '@material-ui/core/';
import useStyles from './styles';

// https://rangle.io/blog/simplifying-controlled-inputs-with-hooks/

export default function StudentAdd(){
  const Airtable = require('airtable');
  const base = new Airtable({apiKey: 'XXXXXXXXXX'}).base('appLNbQOB0WL1ZeDD');

  const  [name,setName] =  useState("");
  const  [age,setAge] =  useState(0);
  const  [city,setCity] =  useState("");
  const classes = useStyles();

  const handleSubmit=(event)=> {
        //event.preventDefault();
    const student={
      Name:name,
      Age:Number(age),
      City:city};
   
    base('Table 1').create(student
      , function(err, record) {
      if (err) {
        console.log(err);
        return;
      }
      console.log(record.getId());
      });
        
  }
           
  return (
    <div>
      <Paper className={classes.root}>
        <form onSubmit={handleSubmit}>
          <TextField id = "name"
            label="Person Name:"
            onChange={e=>setName(e.target.value)}/>
          <TextField id = "age"
            label="Age:"
            onChange={e=>setAge(e.target.value)}/>
          <TextField id = "city"
            label="City:"
            onChange={e=>setCity(e.target.value)}/>
          <Button variant="contained" type="submit" color="primary">Add</Button>
        </form>
      </Paper>
    </div>
  )
}