はじめに
前回までにcontenteditable属性を用いたテキストエディタのHTMLでの作成方法を説明しました。
今度はこのHTMLをASP.NET MVCで簡単に出力できるようなHtmlHelperを作成しようと思います。
実行サンプル
まずは、今回で作成するヘルパーで出力を行うサンプルです。
View(xxxx.cshtml)に以下のような記述を行うことでフォントの指定が可能なテキストエディタを生成します。
1 2 3 4 |
<div class=”form-group> @Html.LabelFor(model => model.Detail, new { @class = "control-label" }) @Html.EditableTextAreaFor(model => model.Detail, new { @class = "form-control", @style = "height:300px"}) </div> |
上記ではModelのDetailプロパティ(string)をヘルパーに渡すことで下記のようなエディタが出力されます。
ヘルパークラスの作成
以下のようなヘルパークラスを作成します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
public static class EditableTextAreaHelper { /// <summary> /// デフォルトフォントサイズ /// </summary> private const int DefaultFontSize = 3; /// <summary> /// 色定義 /// </summary> private static readonly Dictionary<string , string> ColorDef = new Dictionary<string, string>() { {"black", "黒"}, {"blue", "青"}, {"red", "赤"}, {"yellow","黄"}, {"green", "緑"}, }; public static MvcHtmlString EditableTextAreaFor<TModel, TProperty> (this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression, object htmlAttributes = null) { var name = ExpressionHelper.GetExpressionText(expression); var fullName = helper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name); var metadata = ModelMetadata.FromLambdaExpression(expression,helper.ViewData); var value = metadata.Model.ToString(); var editBuilder = new TagBuilder("div"); //太字、イタリックボタンの生成 editBuilder.InnerHtml += GetTextEditButtonTag(); //フォントサイズ、カラーのセレクトボックス生成 editBuilder.InnerHtml += GetFontSizeSelectTag("フォントサイズ:", 7, DefaultFontSize); editBuilder.InnerHtml += GetFontColorSelectTag("フォントカラー:"); //テキストエリア var contentBuilder = new TagBuilder("div"); contentBuilder.Attributes["contenteditable"] = "true"; //隣の隠しテキストエリアにフォーカス移動時にデータを移すためのJavascript contentBuilder.Attributes["onblur"] = "this.nextSibling.innerHTML = this.innerHTML"; //postするためのhiddenのTextAreaを生成 var hiddenText = new TagBuilder("textarea"); hiddenText.Attributes.Add("style", "display: none"); hiddenText.Attributes["name"] = fullName; hiddenText.InnerHtml = value; if (htmlAttributes != null) { contentBuilder.MergeAttributes(new RouteValueDictionary(htmlAttributes)); } contentBuilder.InnerHtml = value; return MvcHtmlString.Create(editBuilder.ToString() + contentBuilder.ToString() + hiddenText.ToString()); } /// <summary> /// テキスト編集ボタン(太字、イタリック)を生成します /// </summary> /// <returns></returns> private static string GetTextEditButtonTag() { var builder = new TagBuilder("div"); //太字 var boldButton = new TagBuilder("button"); boldButton.Attributes.Add("type", "button"); boldButton.Attributes.Add("class", "btn btn-xs btn-primary"); boldButton.Attributes.Add("onclick", "document.execCommand('bold')"); boldButton.SetInnerText("太字"); //イタリック var italicButton = new TagBuilder("button"); italicButton.Attributes.Add("type", "button"); italicButton.Attributes.Add("class", "btn btn-xs btn-primary"); italicButton.Attributes.Add("onclick", "document.execCommand('italic')"); italicButton.SetInnerText("イタリック"); builder.InnerHtml += boldButton.ToString(); builder.InnerHtml += italicButton.ToString(); return builder.ToString(); } /// <summary> /// フォントサイズ選択タグを生成します /// </summary> /// <param name="labelText">ラベル文言</param> /// <param name="maxLevel">最大サイズ</param> /// <param name="defaultLevel">デフォルトサイズ</param> /// <returns></returns> private static string GetFontSizeSelectTag(string labelText, int maxLevel, int defaultLevel) { //ラベル var labelTag = new TagBuilder("label"); labelTag.SetInnerText(labelText); //フォントサイズ選択 var selectTag = new TagBuilder("select"); selectTag.Attributes.Add("onchange","document.execCommand('fontSize',false, this.value)" ); //レベル選択項目作成 foreach (var level in Enumerable.Range(1, maxLevel)) { var optionTag = new TagBuilder("option"); optionTag.Attributes.Add("value", level.ToString()); optionTag.SetInnerText(level.ToString()); //デフォルトレベルであれば選択 if (level == defaultLevel) { optionTag.Attributes.Add("selected", ""); } selectTag.InnerHtml += optionTag.ToString(); } return labelTag.ToString() + selectTag.ToString(); } /// <summary> /// フォントカラー選択タグを生成します /// </summary> /// <param name="labelText">ラベル文言</param> /// <returns></returns> private static string GetFontColorSelectTag(string labelText) { //ラベル var labelTag = new TagBuilder("label"); labelTag.SetInnerText(labelText); //フォント色選択 var selectTag = new TagBuilder("select"); selectTag.Attributes.Add("onchange", "document.execCommand('ForeColor',false, this.value)"); foreach (var color in ColorDef) { var optionTag = new TagBuilder("option"); //色と表示文字の設定 optionTag.Attributes.Add("value", color.Key); optionTag.SetInnerText(color.Value); selectTag.InnerHtml += optionTag.ToString(); } return labelTag.ToString() +selectTag.ToString(); } } |
基本的に前回、前々回で説明したタグを生成しているだけですが、注意点としてcontenteditableを付与したエリアはdivタグで作成されたものなので、そのままではフォームの値としてサーバに送信することはできません。そこでhiddenのtextareaを用意してフォーカス移動時にそこに値を移すように設定しています。
私の使用方法としては確定ボタン→確認DLG→submitといった形になるのでこの方法で問題がありませんが、場合によってはonblurではなく別の方法でhiddenのtextarea等に移す必要があるかもしれません。
1 2 3 4 5 6 7 8 9 10 |
//テキストエリア var contentBuilder = new TagBuilder("div"); contentBuilder.Attributes["contenteditable"] = "true"; //隣の隠しテキストエリアにフォーカス移動時にデータを移すためのJavascript contentBuilder.Attributes["onblur"] = "this.nextSibling.innerHTML = this.innerHTML"; //postするためのhiddenのTextAreaを生成 var hiddenText = new TagBuilder("textarea"); hiddenText.Attributes.Add("style", "display: none"); hiddenText.Attributes["name"] = fullName; hiddenText.InnerHtml = value; |
まとめ
見た目も機能もまだまだ改善の余地ありですが、これで通常のテキストエディタが少しリッチになったと思います。
次回はサーバサイドなどのコードも含めて使い方を進めていこうと思います。