続きです。今回も SharePoint Online のデータへのアクセスが含まれるので、SPFxっぽいかもです。
毎度ながら内容に関して React も TypeScript もあまり上手に使えるわけではないですので、そのあたりご容赦ください。。。
完成イメージ(4/4)
下は画像が荒いですね。
来週の Global Microsoft 365 Developer Bootcamp 前に SharePoint Framework を予習しよう!Teams のタブにもなるよ!
— nan_oka (@nanoka7o8) November 6, 2020
Qiita用にツイート。#OfficeDev #SharePointOnline #SharePointFramework #MicrosoftTeams pic.twitter.com/m4oSyYCAP3
もちろん SharePoint Online のページとしても使えます。
関連記事
Webパーツ初期設定編 1/4
https://qiita.com/nanoka/items/834ee5216a5437dc04bc
画面レイアウトと画面遷移編 2/4
https://qiita.com/nanoka/items/35dea4791ad767497ed6
一覧画面編 3/4
https://qiita.com/nanoka/items/fbda2f09796a445dd9e2
詳細画面編 4/4
https://qiita.com/nanoka/items/92093ca05096728e2deb
手順
1. 新規作成用関数の作成
2. 詳細画面用コンポーネントの新規作成機能の作成
3. 更新、削除用関数の作成
4. 詳細画面用コンポーネントの更新、削除機能の作成
手順の説明
1. 新規作成用関数の作成
データ操作用の関数なので、api フォルダを更新します。
...src/
└ webparts/
└ spfxTodo/
├ api/ (データ操作用関数を管理)
├ assets/
│ └ stylesheets/
├ components/
│ ├ atoms/
│ ├ molecules/ (中くらいの部品を管理)
│ └ organisms/
│ └ pages/ (画面を管理)
│ └ templates/
└ loc/
詳細画面用の単一アイテムを取得する関数と、リストアイテム作成用の関数を作っていきます。
Postなので作成するデータを作ってあげないといけませんが、列名を key 値を value にした Object を渡してあげるだけであとはいい感じにしてくれます。
import { SPHttpClient, SPHttpClientResponse, ISPHttpClientOptions } from '@microsoft/sp-http';
import { WebPartContext } from '@microsoft/sp-webpart-base';
/********** 画面から呼び出し用関数 **********/
/********** TodoList GET **********/
import { SPHttpClient, SPHttpClientResponse, ISPHttpClientOptions } from '@microsoft/sp-http';
import { WebPartContext } from '@microsoft/sp-webpart-base';
import * as util from '../components/util';
import { ITodoItem } from '../components/molecules/ITodoItem';
/********** 画面から呼び出し用関数 **********/
/********** TodoList GET **********/
const getTodoListOptions = "?$select=ID,Title,LimitDate,Note,Modified,Status&$filter=Status eq 'Run'&$orderby=LimitDate asc";
export const GetTodoListItems =
async (setState: any, targetListName: string, context: WebPartContext) => {
setState({ loading: true });
const todoListItems: Array<Object> = await GetListItems(context, targetListName, getTodoListOptions);
setState({ loading: false, todoListItems });
};
/********** TodoDetail GET **********/
export const GetTodoListItemByID =
async (setState: any, targetListName: string, context: WebPartContext, ID: string) => {
setState({ loading: true });
const todoListItem: Object = await GetListItemByID(context, targetListName, ID);
const todoItem: ITodoItem = util.ObjectMerge({ Title: "", LimitDate: null, Note: "", Status: "", ID: "", Created: null, Modified: null }, todoListItem);
setState({ loading: false, iTodoItem: todoItem });
};
/********** TodoDetail POST **********/
export const CreateTodoListItem =
async (setState: any, targetListName: string, context: WebPartContext, todoItem: ITodoItem) => {
setState({ loading: true });
const createTodoItem: ITodoItem = util.ObjectMerge({ Title: "", LimitDate: null, Note: '', Status: '' }, todoItem);
createTodoItem.Status = "Run";
const res = await CreateListItem(context, targetListName, createTodoItem);
};
/********** リストアイテムの操作用共通関数 **********/
const defHeaders: HeadersInit = { "Content-type": "application/json", "Accept": "application/json" };
/********** 検索 **********/
const GetListItems =
async (context: WebPartContext, listName: string, options: string) => {
if (!options) {
options = "";
}
const restUri: string = `${context.pageContext.web.absoluteUrl}/_api/web/lists/getbytitle('${listName}')/Items${options}`;
const res: SPHttpClientResponse = await SpRestGet(context, restUri);
const resJson: any = await res.json();
const resJsonArray: Array<Object> = resJson.value;
return resJsonArray;
};
const GetListItemByID =
async (context: WebPartContext, listName: string, ID: string) => {
const restUri: string = `${context.pageContext.web.absoluteUrl}/_api/web/lists/getbytitle('${listName}')/Items(${ID})`;
const res: SPHttpClientResponse = await SpRestGet(context, restUri);
const resJson: any = await res.json();
return resJson;
};
/********** 作成 **********/
const CreateListItem = async (context: WebPartContext, listName: string, dataJson: Object) => {
const restUri: string = `${context.pageContext.web.absoluteUrl}/_api/web/lists/getbytitle('${listName}')/Items`;
const options: ISPHttpClientOptions = {
body: JSON.stringify(dataJson),
headers: defHeaders
};
const res: SPHttpClientResponse = await SpRestPost(context, restUri, options);
const resJson: any = await res.json();
return resJson;
};
/********** 更新 **********/
/********** 削除 **********/
/********** Spへのアクセス用共通関数 **********/
/********** GET Request **********/
const SpRestGet =
async (context: WebPartContext, RestUri: string): Promise<SPHttpClientResponse> => {
const res: SPHttpClientResponse = await context.spHttpClient.get(RestUri, SPHttpClient.configurations.v1);
//エラーチェックは他のサイトが詳しいので省きます
return res;
};
/********** POST Request **********/
const SpRestPost =
async (context: WebPartContext, RestUri: string, options: ISPHttpClientOptions): Promise<SPHttpClientResponse> => {
const res: SPHttpClientResponse = await context.spHttpClient.post(RestUri, SPHttpClient.configurations.v1, options);
//エラーチェックは外部サイトが詳しいので省きます
return res;
};
2. 詳細画面用コンポーネントの新規作成機能の作成
次に先ほどの関数を画面から呼び出します。
今回は入力フォームの部分だけ、TodoItemForm として子コンポーネントに切り出しました。
関数の呼び出しは親コンポーネントで定義して、関数ごと子コンポーネントに渡す方針とします。
import * as React from 'react';
import { Stack, IStackTokens } from 'office-ui-fabric-react';
import Loading from '../molecules/Loading';
import * as api from '../../api';
import TodoItemForm from '../molecules/TodoItemForm';
import { IPageProps } from './IPageProps';
import { ITodoItem } from '../molecules/ITodoItem';
interface State {
loading: boolean;
formType: "new" | "edit";
iTodoItem: ITodoItem;
}
class TodoDetail extends React.Component<IPageProps, State> {
constructor(props: IPageProps) {
super(props);
this.state = {
loading: false, formType: "new",
iTodoItem: { Title: "", LimitDate: new Date().toJSON(), Note: "", Status: "", ID: "", Created: null, Modified: null }
};
}
private CreateTodoListItem = async (todoItem: ITodoItem) => {
await api.CreateTodoListItem(this.setState.bind(this), this.props.todoListName, this.props.webPartContext, todoItem);
this.props.routeProps.history.push({ pathname: "/" });
}
private GetTodoListItemByID = async () =>
await api.GetTodoListItemByID(this.setState.bind(this), this.props.todoListName, this.props.webPartContext, this.props.routeProps.match.params.ID)
private HistoryPushTodoList = () => {
this.props.routeProps.history.push({ pathname: "/" });
}
public render() {
const stackTokens: IStackTokens = { childrenGap: 10 };
const { routeProps } = this.props;
const { match } = routeProps;
const { loading, iTodoItem } = this.state;
return (
<div>
<Stack tokens={stackTokens}>
<Stack.Item align="auto">
{loading ? (
<Loading />
) : (
<TodoItemForm itemID={match.params.ID || null} formType={match.params.ID ? "edit" : "new"} iTodoItem={iTodoItem}
CreateTodoListItem={this.CreateTodoListItem} GetTodoListItemByID={this.GetTodoListItemByID}
HistoryPushTodoList={this.HistoryPushTodoList} />
)}
</Stack.Item>
</Stack>
</div>
);
}
}
export default TodoDetail;
import { WebPartContext } from '@microsoft/sp-webpart-base';
import { ITodoItem } from './ITodoItem';
export interface ITodoItemFormProps {
itemID: string;
formType: "new" | "edit";
iTodoItem: ITodoItem;
CreateTodoListItem: Function;
GetTodoListItemByID: Function;
HistoryPushTodoList: Function;
}
import * as React from 'react';
import {
PrimaryButton, DefaultButton, Stack, IStackTokens, DatePicker, TextField, Separator
} from 'office-ui-fabric-react';
import * as util from '../util';
import { ITodoItemFormProps } from './ITodoItemFormProps';
import { ITodoItem } from './ITodoItem';
class TodoItemForm extends React.Component<ITodoItemFormProps, ITodoItem> {
constructor(props: ITodoItemFormProps) {
super(props);
this.state = this.props.iTodoItem;
}
private CreateTodoListItem = async (e, itodoItem: ITodoItem) => {
e.preventDefault();
await this.props.CreateTodoListItem(itodoItem);
}
private GetTodoListItemByID = async () => this.props.GetTodoListItemByID();
private HistoryPushTodoList = () => this.props.HistoryPushTodoList();
public async componentDidMount() {
if (this.props.formType == "edit" && !(this.state.ID)) {
await this.GetTodoListItemByID();
}
}
public render() {
const stackTokens: IStackTokens = { childrenGap: 10 };
const { formType } = this.props;
const { Title, LimitDate, Note, ID, Created, Modified } = this.state;
return (
<div>
<form>
<Stack horizontal tokens={stackTokens}>
<Stack.Item align="auto" grow={4}>
<TextField label="件名" value={Title} onChange={(e, newVal) => this.setState({ Title: newVal })} />
</Stack.Item>
<Stack.Item align="auto" grow={2}>
<DatePicker label="期限" value={new Date(LimitDate)}
formatDate={(dispDate) => util.DateFormatJa(dispDate.toJSON())}
onSelectDate={(newDate) => this.setState({ LimitDate: newDate.toJSON() })} />
</Stack.Item>
</Stack>
<Stack horizontal tokens={stackTokens} >
<Stack.Item align="auto" grow={6}>
<TextField label="メモ" value={Note} multiline rows={6} resizable={false}
onChange={(e, newVal) => this.setState({ Note: newVal })} />
</Stack.Item>
</Stack>
<Separator />
<Stack horizontal tokens={stackTokens} >
<Stack.Item align="auto" grow={2}>
<TextField label="ID" value={ID || "New"} borderless={true} readOnly={true} />
</Stack.Item>
<Stack.Item align="auto" grow={2}>
<TextField label="作成日時" value={Created ? util.DateTimeFormatJa(Created) : "-"} borderless={true} readOnly={true} />
</Stack.Item>
<Stack.Item align="auto" grow={2}>
<TextField label="更新日時" value={Modified ? util.DateTimeFormatJa(Modified) : "-"} borderless={true} readOnly={true} />
</Stack.Item>
</Stack>
<Separator />
<Stack tokens={stackTokens}>
<Stack horizontal horizontalAlign="end" tokens={stackTokens}>
<Stack.Item align="auto">
<PrimaryButton type="submit" value="Create" onClick={(e) => this.CreateTodoListItem(e, this.state)} disabled={formType === "edit"} text="Create" />
</Stack.Item>
</Stack>
<Stack horizontal horizontalAlign="end" tokens={stackTokens}>
<Stack.Item align="auto">
<DefaultButton text="Back" onClick={this.HistoryPushTodoList} />
</Stack.Item>
</Stack>
</Stack>
</form>
</div>
);
}
}
export default TodoItemForm;
Hosted workbench の実行で以下のように表示できていれば成功です。
Createボタンをクリックするとアイテムが作成されると思います。
3. 更新、削除用関数の作成
ほとんど同じコードですが、更新、削除も作成します。
import { SPHttpClient, SPHttpClientResponse, ISPHttpClientOptions } from '@microsoft/sp-http';
import { WebPartContext } from '@microsoft/sp-webpart-base';
import * as util from '../components/util';
import { ITodoItem } from '../components/molecules/ITodoItem';
/********** 画面から呼び出し用関数 **********/
/********** TodoList GET **********/
const getTodoListOptions = "?$select=ID,Title,LimitDate,Note,Modified,Status&$filter=Status eq 'Run'&$orderby=LimitDate asc";
export const GetTodoListItems =
async (setState: any, targetListName: string, context: WebPartContext) => {
setState({ loading: true });
const todoListItems: Array<Object> = await GetListItems(context, targetListName, getTodoListOptions);
setState({ loading: false, todoListItems });
};
/********** TodoDetail GET **********/
export const GetTodoListItemByID =
async (setState: any, targetListName: string, context: WebPartContext, ID: string) => {
setState({ loading: true });
const todoListItem: Object = await GetListItemByID(context, targetListName, ID);
const todoItem: ITodoItem = util.ObjectMerge({ Title: "", LimitDate: null, Note: "", Status: "", ID: "", Created: null, Modified: null }, todoListItem);
setState({ loading: false, iTodoItem: todoItem });
};
/********** TodoDetail POST **********/
export const CreateTodoListItem =
async (setState: any, targetListName: string, context: WebPartContext, todoItem: ITodoItem) => {
setState({ loading: true });
const createTodoItem: ITodoItem = util.ObjectMerge({ Title: "", LimitDate: null, Note: '', Status: '' }, todoItem);
createTodoItem.Status = "Run";
const res = await CreateListItem(context, targetListName, createTodoItem);
};
export const UpdateTodoListItem =
async (setState: any, targetListName: string, context: WebPartContext, todoItem: ITodoItem) => {
setState({ loading: true });
const updateTodoItem: ITodoItem = util.ObjectMerge({ Title: "", LimitDate: null, Note: '', Status: '' }, todoItem);
const res = await UpdateListItem(context, targetListName, todoItem.ID, updateTodoItem);
};
export const CompleteTodoListItem =
async (setState: any, targetListName: string, context: WebPartContext, todoItem: ITodoItem) => {
setState({ loading: true });
const updateTodoItem: ITodoItem = util.ObjectMerge({ Title: "", LimitDate: null, Note: '', Status: '' }, todoItem);
updateTodoItem.Status = "Done";
const res = await UpdateListItem(context, targetListName, todoItem.ID, updateTodoItem);
};
export const DeleteTodoListItem =
async (setState: any, targetListName: string, context: WebPartContext, todoItem: ITodoItem) => {
setState({ loading: true });
const res = await DeleteListItem(context, targetListName, todoItem.ID);
};
/********** リストアイテムの操作用共通関数 **********/
const defHeaders: HeadersInit = { "Content-type": "application/json", "Accept": "application/json" };
const updHeaders: HeadersInit = { "Content-type": "application/json", "Accept": "application/json", "X-HTTP-Method": "MERGE", "If-Match": "*" };
const delHeaders: HeadersInit = { "Content-type": "application/json", "Accept": "application/json", "X-HTTP-Method": "DELETE", "If-Match": "*" };
/********** 検索 **********/
const GetListItems =
async (context: WebPartContext, listName: string, options: string) => {
if (!options) {
options = "";
}
const restUri: string = `${context.pageContext.web.absoluteUrl}/_api/web/lists/getbytitle('${listName}')/Items${options}`;
const res: SPHttpClientResponse = await SpRestGet(context, restUri);
const resJson: any = await res.json();
const resJsonArray: Array<Object> = resJson.value;
return resJsonArray;
};
const GetListItemByID =
async (context: WebPartContext, listName: string, ID: string) => {
const restUri: string = `${context.pageContext.web.absoluteUrl}/_api/web/lists/getbytitle('${listName}')/Items(${ID})`;
const res: SPHttpClientResponse = await SpRestGet(context, restUri);
const resJson: any = await res.json();
return resJson;
};
/********** 作成 **********/
const CreateListItem = async (context: WebPartContext, listName: string, dataJson: Object) => {
const restUri: string = `${context.pageContext.web.absoluteUrl}/_api/web/lists/getbytitle('${listName}')/Items`;
const options: ISPHttpClientOptions = {
body: JSON.stringify(dataJson),
headers: defHeaders
};
const res: SPHttpClientResponse = await SpRestPost(context, restUri, options);
const resJson: any = await res.json();
return resJson;
};
/********** 更新 **********/
const UpdateListItem = async (context: WebPartContext, listName: string, ID: string, dataJson: Object) => {
const restUri: string = `${context.pageContext.web.absoluteUrl}/_api/web/lists/getbytitle('${listName}')/Items(${ID})`;
const options: ISPHttpClientOptions = {
body: JSON.stringify(dataJson),
headers: updHeaders
};
const res: SPHttpClientResponse = await SpRestPost(context, restUri, options);
return res;
};
/********** 削除 **********/
const DeleteListItem = async (context: WebPartContext, listName: string, ID: string) => {
const restUri: string = `${context.pageContext.web.absoluteUrl}/_api/web/lists/getbytitle('${listName}')/Items(${ID})`;
const options: ISPHttpClientOptions = {
headers: delHeaders
};
const res: SPHttpClientResponse = await SpRestPost(context, restUri, options);
return res;
};
/********** Spへのアクセス用共通関数 **********/
/********** GET Request **********/
const SpRestGet =
async (context: WebPartContext, RestUri: string): Promise<SPHttpClientResponse> => {
const res: SPHttpClientResponse = await context.spHttpClient.get(RestUri, SPHttpClient.configurations.v1);
//エラーチェックは外部サイトが詳しいので省きます
return res;
};
/********** POST Request **********/
const SpRestPost =
async (context: WebPartContext, RestUri: string, options: ISPHttpClientOptions): Promise<SPHttpClientResponse> => {
const res: SPHttpClientResponse = await context.spHttpClient.post(RestUri, SPHttpClient.configurations.v1, options);
//エラーチェックは外部サイトが詳しいので省きます
return res;
};
4. 詳細画面用コンポーネントの更新、削除機能の作成
ここもほとんど同じです。更新、削除関数の呼び出しを追加していきます。
import * as React from 'react';
import { Stack, IStackTokens } from 'office-ui-fabric-react';
import Loading from '../molecules/Loading';
import * as api from '../../api';
import TodoItemForm from '../molecules/TodoItemForm';
import { IPageProps } from './IPageProps';
import { ITodoItem } from '../molecules/ITodoItem';
interface State {
loading: boolean;
formType: "new" | "edit";
iTodoItem: ITodoItem;
}
class TodoDetail extends React.Component<IPageProps, State> {
constructor(props: IPageProps) {
super(props);
this.state = {
loading: false, formType: "new",
iTodoItem: { Title: "", LimitDate: new Date().toJSON(), Note: "", Status: "", ID: "", Created: null, Modified: null }
};
}
private CreateTodoListItem = async (todoItem: ITodoItem) => {
await api.CreateTodoListItem(this.setState.bind(this), this.props.todoListName, this.props.webPartContext, todoItem);
this.props.routeProps.history.push({ pathname: "/" });
}
private UpdateTodoListItem = async (todoItem: ITodoItem) => {
await api.UpdateTodoListItem(this.setState.bind(this), this.props.todoListName, this.props.webPartContext, todoItem);
this.props.routeProps.history.push({ pathname: "/" });
}
private CompleteTodoListItem = async (todoItem: ITodoItem) => {
await api.CompleteTodoListItem(this.setState.bind(this), this.props.todoListName, this.props.webPartContext, todoItem);
this.props.routeProps.history.push({ pathname: "/" });
}
private DeleteTodoListItem = async (todoItem: ITodoItem) => {
await api.DeleteTodoListItem(this.setState.bind(this), this.props.todoListName, this.props.webPartContext, todoItem);
this.props.routeProps.history.push({ pathname: "/" });
}
private GetTodoListItemByID = async () =>
await api.GetTodoListItemByID(this.setState.bind(this), this.props.todoListName, this.props.webPartContext, this.props.routeProps.match.params.ID)
private HistoryPushTodoList = () => {
this.props.routeProps.history.push({ pathname: "/" });
}
public render() {
const stackTokens: IStackTokens = { childrenGap: 10 };
const { routeProps } = this.props;
const { match } = routeProps;
const { loading, iTodoItem } = this.state;
return (
<div>
<Stack tokens={stackTokens}>
<Stack.Item align="auto">
{loading ? (
<Loading />
) : (
<TodoItemForm itemID={match.params.ID || null} formType={match.params.ID ? "edit" : "new"} iTodoItem={iTodoItem}
CreateTodoListItem={this.CreateTodoListItem} UpdateTodoListItem={this.UpdateTodoListItem}
CompleteTodoListItem={this.CompleteTodoListItem} DeleteTodoListItem={this.DeleteTodoListItem}
GetTodoListItemByID={this.GetTodoListItemByID} HistoryPushTodoList={this.HistoryPushTodoList} />
)}
</Stack.Item>
</Stack>
</div>
);
}
}
export default TodoDetail;
import { WebPartContext } from '@microsoft/sp-webpart-base';
import { ITodoItem } from './ITodoItem';
export interface ITodoItemFormProps {
itemID: string;
formType: "new" | "edit";
iTodoItem: ITodoItem;
CreateTodoListItem: Function;
UpdateTodoListItem: Function;
CompleteTodoListItem: Function;
DeleteTodoListItem: Function;
GetTodoListItemByID: Function;
HistoryPushTodoList: Function;
}
import * as React from 'react';
import {
PrimaryButton, DefaultButton, Stack, IStackTokens, DatePicker, TextField, Separator
} from 'office-ui-fabric-react';
import * as util from '../util';
import { ITodoItemFormProps } from './ITodoItemFormProps';
import { ITodoItem } from './ITodoItem';
class TodoItemForm extends React.Component<ITodoItemFormProps, ITodoItem> {
constructor(props: ITodoItemFormProps) {
super(props);
this.state = this.props.iTodoItem;
}
private CreateTodoListItem = async (e, itodoItem: ITodoItem) => {
e.preventDefault();
await this.props.CreateTodoListItem(itodoItem);
}
private UpdateTodoListItem = async (e, itodoItem: ITodoItem) => {
e.preventDefault();
await this.props.UpdateTodoListItem(itodoItem);
}
private CompleteTodoListItem = async (e, itodoItem: ITodoItem) => {
e.preventDefault();
await this.props.CompleteTodoListItem(itodoItem);
}
private DeleteTodoListItem = async (e, itodoItem: ITodoItem) => {
e.preventDefault();
await this.props.DeleteTodoListItem(itodoItem);
}
private GetTodoListItemByID = async () => this.props.GetTodoListItemByID();
private HistoryPushTodoList = () => this.props.HistoryPushTodoList();
public async componentDidMount() {
if (this.props.formType == "edit" && !(this.state.ID)) {
await this.GetTodoListItemByID();
}
}
public render() {
const stackTokens: IStackTokens = { childrenGap: 10 };
const { formType } = this.props;
const { Title, LimitDate, Note, ID, Created, Modified } = this.state;
return (
<div>
<form>
<Stack horizontal tokens={stackTokens}>
<Stack.Item align="auto" grow={4}>
<TextField label="件名" value={Title} onChange={(e, newVal) => this.setState({ Title: newVal })} />
</Stack.Item>
<Stack.Item align="auto" grow={2}>
<DatePicker label="期限" value={new Date(LimitDate)}
formatDate={(dispDate) => util.DateFormatJa(dispDate.toJSON())}
onSelectDate={(newDate) => this.setState({ LimitDate: newDate.toJSON() })} />
</Stack.Item>
</Stack>
<Stack horizontal tokens={stackTokens} >
<Stack.Item align="auto" grow={6}>
<TextField label="メモ" value={Note} multiline rows={6} resizable={false}
onChange={(e, newVal) => this.setState({ Note: newVal })} />
</Stack.Item>
</Stack>
<Separator />
<Stack horizontal tokens={stackTokens} >
<Stack.Item align="auto" grow={2}>
<TextField label="ID" value={ID || "New"} borderless={true} readOnly={true} />
</Stack.Item>
<Stack.Item align="auto" grow={2}>
<TextField label="作成日時" value={Created ? util.DateTimeFormatJa(Created) : "-"} borderless={true} readOnly={true} />
</Stack.Item>
<Stack.Item align="auto" grow={2}>
<TextField label="更新日時" value={Modified ? util.DateTimeFormatJa(Modified) : "-"} borderless={true} readOnly={true} />
</Stack.Item>
</Stack>
<Separator />
<Stack tokens={stackTokens}>
<Stack horizontal horizontalAlign="end" tokens={stackTokens}>
<Stack.Item align="auto">
<PrimaryButton type="submit" value="Create" onClick={(e) => this.CreateTodoListItem(e, this.state)} disabled={formType === "edit"} text="Create" />
</Stack.Item>
<Stack.Item align="auto">
<PrimaryButton type="submit" value="Update" onClick={(e) => this.UpdateTodoListItem(e, this.state)} disabled={formType === "new"} text="Update" />
</Stack.Item>
<Stack.Item align="auto">
<PrimaryButton type="submit" value="Complete" onClick={(e) => this.CompleteTodoListItem(e, this.state)} disabled={formType === "new"} text="Complete(Update)" />
</Stack.Item>
<Stack.Item align="auto">
<PrimaryButton type="submit" value="Delete" onClick={(e) => this.DeleteTodoListItem(e, this.state)} disabled={formType === "new"} text="Delete" />
</Stack.Item>
</Stack>
<Stack horizontal horizontalAlign="end" tokens={stackTokens}>
<Stack.Item align="auto">
<DefaultButton text="Back" onClick={this.HistoryPushTodoList} />
</Stack.Item>
</Stack>
</Stack>
</form>
</div>
);
}
}
export default TodoItemForm;
Hosted workbench の実行で完成イメージ(4/4)みたいに表示されれば完成です。
お疲れさまでした!
アプリが完成したら、実際に展開したくなると思います。
その手順はまたまた毎度引用ばかりになりますが、以下を参考にされるといいと思います。
以下に実際に動作させたファイルを置いておきますので、うまくいかない場合は参考にしてください。
アプリとして不十分な部分はたくさんありますが、基本的な操作を網羅できたかと思います。
あくまで一つの例として参考になれば幸いです。
みなさまもぜひ、いろんなアプリを作ってみてください。