前回のおさらい
https://qiita.com/CheerfulGeneral/items/be34fcccfe397c27d064
1行掲示板を
モダンになったASP.net coreで作ってみようという試みです。
前回は
・Model(掲示板データ)を定義
・データベースのマイグレーション(初期化)
・スキャフォールディング(骨組みページ)の作成
を行いました。
今回はスキャフォールディングを編集して、掲示板を完成させたいと思います。
軽くRazorも見てみましょう。
1.CRUDページ
https://wa3.i-3-i.info/word123.html
CRUD(クラッドと読むらしい)とは
Create, Read, Update, Delete
の頭文字であり、要するにデータを作ったり処理することです。
スキャフォールディングを作成すると、このCRUDページが自動作成されます。
以下の名前でページが作成されています。
・Index (データを表示させるページ CRUDのR)
・Create (データを作成するページ CRUDのC)
・Edit (データを編集するページ CRUDのU)
・Detail (データを詳細にみるページ CRUDのR 2つめ)
・Delete (データを削除するページ CRUDのD)
前回はIndexとCreateをそのまま表示させて
1行掲示板みたいに使いました。
今回はこの2つをベースに、もっと掲示板ぽくしてみようと思いいます。
2.Indexページから見るRazorコード
@page
@model RasorSample.Pages.BBS.IndexModel
@{
ViewData["Title"] = "1行掲示板";
}
<h1>1行掲示板</h1>
<p>
<a asp-page="Create">投稿する</a>
</p>
<table class="table">
<tbody>
@foreach (var item in Model.Writing) {
<tr>
<td>
<h2>@Html.DisplayFor(modelItem => item.Name)</h2>
</td>
<td>
<h2>@Html.DisplayFor(modelItem => item.Comment)</h2>
</td>
<td>
@Html.DisplayFor(modelItem => item.ReleaseDate)
</td>
<td>
<a asp-page="./Edit" asp-route-id="@item.ID">編集</a> |
<a asp-page="./Delete" asp-route-id="@item.ID">削除</a>
</td>
</tr>
}
</tbody>
</table>
Indexページを上記のように改修しました。
ページは.cshtmlという拡張子ファイルで出来います。
改修箇所とRazor
Indexで改修したのは
1.Tableのtheadを消去して、掲示板ぽくした。
2.Titleおよび他用語の日本語化
です。
Razorの形式を見ながら、改修箇所を見ていきましょう。
@page
@page
Razorはアットマーク(@)の後に
コードを書く形式となっております。
@pageは、このファイルがRazorですよーという合図のようです。
@model
@model RasorSample.Pages.BBS.IndexModel
モデルを参照するコードです。
WPFの開発をしたことがる人はわかるかもしれませんが、
Razorページは.cshtmlの裏にC#ファイル(.cshtml.cs)があります。
このC#ファイルのクラスが「IndexModel」という名前です。
モデルをC#で扱う形式に変換するというイメージかもしれません。
@ViewData
@{
ViewData["Title"] = "1行掲示板";
}
@の中の「ViewData["Title"]でページタイトルを編集します。
ViewDataというのはページ表示項目を集めた辞書となっており
その"Title"キーの値を編集します。
asp属性
htmlのaタグに「asp-○○」という属性があります。
<a asp-page="Create">投稿する</a>
<a asp-page="./Edit" asp-route-id="@item.ID">編集</a> |
asp-pageは指定したアドレスに移行します。
(FormのGetメソッドみたい)
asp-route-idはルートidをつける属性みたいです。
ルートidを調べてみましたが、よくわかりませんでした...
(ページ移行アクションで間違いがないように、IDを送るみたいですが...)
この辺りを改修すれば、掲示板みたいな表示になりました。
3.Createページ
@page
@model RasorSample.Pages.BBS.CreateModel
@{
ViewData["Title"] = "投稿ページ";
}
<h1>投稿する</h1>
<hr />
<div class="row">
<div class="col-md-4">
<form method="post">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Writing.Name" class="control-label"></label>
<input asp-for="Writing.Name" class="form-control" />
<span asp-validation-for="Writing.Name" class="text-danger"></span>
</div>
<div class="form-group">
<label class="control-label">書き込み内容</label>
<input asp-for="Writing.Comment" class="form-control" />
<span asp-validation-for="Writing.Comment" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Writing.ReleaseDate" class="control-label"></label>
<input asp-for="Writing.ReleaseDate" class="form-control" />
<span asp-validation-for="Writing.ReleaseDate" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="投稿" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-page="Index">掲示板に戻る</a>
</div>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
記入したデータをCreate(掲示板に投稿)する画面です。
基本的に改修はIndexと同様に
必要な表示箇所を直すくらいです。
ここでは、Indexでは見なかったC#ファイルを見てみましょう。
using RasorSample.Model;
namespace RasorSample.Pages.BBS
{
public class CreateModel : PageModel
{
private readonly RasorSample.Data.RasorSampleContext _context;
public CreateModel(RasorSample.Data.RasorSampleContext context)
{
_context = context;
}
public IActionResult OnGet()
{
return Page();
}
[BindProperty]
public Writing Writing { get; set; }
// To protect from overposting attacks, see https://aka.ms/RazorPagesCRUD
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Writing.Add(Writing);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
}
}
上の方ではcontext(ページ内容を含むクラス?)を作成しています。
OnGetメソッドでページを初期化します。
真ん中の[BindProperty]では、作ったModelクラスのプロパティを作成しております。
Createで重要なのは OnPostAsync()メソッドです。
// To protect from overposting attacks, see https://aka.ms/RazorPagesCRUD
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Writing.Add(Writing);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
これはページでデータが送信された際に処理されます。
データフォームが正常であれば、Post送信されてIndexに戻ります。
4.その他ページ・通常MVCとの違い
CRUDには他にもページがありますが、
Indexなどの要領で同様に改修しました。
ASP.net coreではスキャフォールディングを作成すれば
Webアプリの基本動作は自動で実装してくれます。
他Webフレームワークのように、最初からViewやControllerを作らなくても
簡単にWebアプリケーションを作れてしまいます。
Createの箇所を見て気づいた方がいらっしゃると思いますが
RazorでWebアプリを作成する場合
1ページ(1 View)に1つのC#ファイル(1 Controller)が付きます。
そのため、ページごとに動作・Controllerを書きます。
通常のMVCはControllerを一括で書いて、
ルーティングごとにViewの切り替えをしていたと思います。
ここがRazorと通常MVCフレームワークの違いです。
個人的にはRazorの方が作りやすいなーと感じました。
(WPFの経験があるのも関係あるかもしれません。)
5.IndexとCreateをくっつけてみた
これで1行掲示板になった!と安心していましたが
第1回で載せた掲示板イメージはこんなのでした。
てなわけで、イメージ通りになるように
IndexとCreateをくっつけてみました。
@page
@model RasorSample.Pages.BBS.IndexModel
@{
ViewData["Title"] = "1行掲示板";
}
<h1>1行掲示板</h1>
<div class="row">
<div class="col-3"></div>
<div class="col-6">
<form method="post">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="WritingCreator.Name" class="control-label"></label>
<input asp-for="WritingCreator.Name" class="form-control" />
<span asp-validation-for="WritingCreator.Name" class="text-danger"></span>
</div>
<div class="form-group">
<label class="control-label">書き込み内容</label>
<input asp-for="WritingCreator.Comment" class="form-control" />
<span asp-validation-for="WritingCreator.Comment" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="投稿" class="btn btn-primary" />
</div>
</form>
</div>
<div class="col-3"></div>
</div>
<table class="table">
<tbody>
@foreach (var item in Model.Writing) {
<tr>
<td>
<h2>@Html.DisplayFor(modelItem => item.Name)</h2>
</td>
<td>
<h2>@Html.DisplayFor(modelItem => item.Comment)</h2>
</td>
<td>
@Html.DisplayFor(modelItem => item.ReleaseDate)
</td>
<td>
<a asp-page="./Edit" asp-route-id="@item.ID">編集</a> |
<a asp-page="./Delete" asp-route-id="@item.ID">削除</a>
</td>
</tr>
}
</tbody>
</table>
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using RasorSample.Model;
namespace RasorSample.Pages.BBS
{
public class IndexModel : PageModel
{
private readonly RasorSample.Data.RasorSampleContext _context;
public IndexModel(RasorSample.Data.RasorSampleContext context)
{
_context = context;
}
public IList<Writing> Writing { get;set; } //For Index
[BindProperty]
public Writing WritingCreator { get; set; } //For Create
public async Task OnGetAsync() //For Index
{
Writing = await _context.Writing.ToListAsync();
}
// To protect from overposting attacks, see https://aka.ms/RazorPagesCRUD
public async Task<IActionResult> OnPostAsync() //For Create
{
if (!ModelState.IsValid)
{
return Page();
}
WritingCreator.ReleaseDate = DateTime.Now; //投稿時の日付を自動記載
_context.Writing.Add(WritingCreator);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
}
}
IndexにCreateを貼り付けただけですね(笑
日付の選択が面倒だと思ったので、書き込み時の日付を
自動的に記入するようにしました。
スキャフォールディングで素材が全てそろっているので
コピペだけでもここまで出来てしまいます。
(余談)cshtmlを編集していた際に、col-○○を使用して思いましたが
このRazorページってBootstrapを標準装備しているんですね。
(ASP.netの標準装備に同様なcolクラスがあるのかもしれないが)
まとめと今後の展望
Razorでスキャフォールディングを使えば
とても楽にMVCのWebアプリが作れました。
今度はSPA(シングルページアプリケーション)に挑戦してみたいと思います。
私も勉強しながら投稿しているため、
随分知識不足で正しくなさそうな書き込みとなってしまいました...oTL
もし間違いや指摘がありましたら、遠慮なくコメントお願いします。
また、ご存じの方がいらしましたら下記の情報提供をお願いします。
・ASP.net core 5.0のアプリを公開できるシステムorレンタルサーバー
本来はVPSやクラウドに実行環境載せるのだと思いますが、このあたりの知識がまだ乏しくて...
・ASP.net core 5.0でMySQLを使う
調査してみましたが、Web勉強中素人の私ではよく扱えませんでした...
(この辺りはASP.net core 5.0がリリースしてまだ3カ月しか経ってないため
情報もそんなないと思いますが...)
参考文献
https://demi-urge.com/razor-mvc-difference/
MVCとRazorの違いがわかりやすく図示されております。
https://github.com/Sho-Zhao/RasorSample
GitHubにRazorで作った1行掲示板をアップロードしました。