Quantcast
Channel: CSSタグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 8578

CSS設計完全ガイドで学んだPRE_CSSをElmで堅牢に実装する

$
0
0

皆さんは、CSSが得意ですか? 私は大の苦手で、ずっと避け続けています。しかし、何度も何度も向きあおうとしていました。もし共感していただける方には朗報です。

CSS設計完全ガイド

と言う懇切丁寧で、PRE_CSSと言うCSSモジュール設計(OOCSSやBEMなどの仲間です)の設計者本人が書かれた書籍になります。まだ読み初めではありますが、とても感銘を受けた書籍のためレビューの代わりに、Elmでとにかく実践してみると言う形で恩返しをさせていただきます。興味が湧かれた方は是非、購入を検討してみてください。

今回の記事は、肩慣らしということでエレメントモジュール(3.2参照)のボタンに焦点を当ててみました。コードは、こちら

Image from Gyazo

まずは、愚直に実装してみる

今回対象となるボタンは「標準ボタン」「標準ボタンのDisabled状態」「前にアイコンがあるボタン」の三つになります。今回はViewだけに焦点を当てるため、Modelはどのボタンが押されたかの状態を表すもの。Msgもそれに合わせたものになります。

moduleMainexposing(main)importBrowserimportHtmlexposing(Html,a,article,div,p,text)importHtml.Attributesexposing(class,href)importHtml.Eventsexposing(onClick)-- MAINmain:Program()ModelMsgmain=Browser.element{init=init,update=update,view=view,subscriptions=subscriptions}-- MODELtypeClickState=Standard|Downloading|UnClicktypealiasModel=ClickStateinit:()->(Model,CmdMsg)init_=(UnClick,Cmd.none)-- UPDATEtypeMsg=StandardClick|DownloadClickupdate:Msg->Model->(Model,CmdMsg)updatemsg_=casemsgofStandardClick->(Standard,Cmd.none)DownloadClick->(Downloading,Cmd.none)-- VIEWview:Model->HtmlMsgviewmodel=article[class"ly_cont"][div[class"bl_buttonSimulation"][a[class"el_btn",onClickStandardClick,href"#"][text"標準ボタン"],a[class"el_btn el_btn__disabled",href"#"][text"標準ボタン"],a[class"el_beforeIconBtn el_beforeIconBtn__download",onClickDownloadClick,href"#"][text"ダウンロード"],p[class"bl_buttonSimulation_clickState"][text<|casemodelofStandard->"標準ボタンがクリックされました"Downloading->"ダウンロードボタンがクリックされました"UnClick->""]]]subscriptions:Model->SubMsgsubscriptions_=Sub.none

エレメントモジュールのように、どのページ・レイアウトでも使われる可能性があるため、上記のコードのように剥き出しのHTMLタグのままコピー&ペーストを繰り返して開発・運用してしまうと変更に弱く・抽象度が低いためコードの見通しも悪くなってしまいます。

エレメントモジュールを疎結合にする

ElmはSPA時のページ単位ぐらいの粒度以外でのコンポーネント指向は現在推奨されていません(推奨されている時代もありましたが)。しかし、常にコードを具体的に書くという意味ではなく、むしろエレメントモジュールのようなHtml msgについては積極的にモジュール分割や関数による抽象化を推し進めるべきでしょう。今回は、アイコンとボタンにフォーカスを当てて分割と抽象化を行いました。

一つのアプリケーションにおけるアイコンの使用量は基本的に現実的な数で納まり、使用箇所や変更が簡単に行えると実に堅牢です。愚直に書くケースではclass文字列でコードが散りばめられる可能性がありましたが、カスタムタイプとモディファイア(3.3参照)への変換関数を用いて使用することで安全に使用することができます。

moduleElement.Iconexposing(Icon(..),toButtonModifier)typeIcon=DownloadtoButtonModifier:Icon->StringtoButtonModifiericon=caseiconofDownload->"download"

ボタン用のモジュールを作り、画面ごとのオレオレボタンが生成されるのを防ぎましょう。Disabled状態のボタンはMsgを発行される必要がないためプロパティとしてdisabledフラグを受け取ることで、onClickを持つボタンとそうでないボタンに分岐してカプセル化を行いました。beforeIconButtonは先ほど定義したIconを受け取り中で変換することで、確実に存在するアイコンだけを使うことができるようになりました。とても堅牢です。

moduleElement.Buttonexposing(beforeIconButton,standardButton)importElement.IconasIconimportHtmlexposing(Html,a,text)importHtml.Attributesexposing(class,href)importHtml.Eventsexposing(onClick)typealiasButtonPropsmsg={text:String,onClick:msg,disabled:Bool}standardButton:ButtonPropsmsg->HtmlmsgstandardButtonprops=ifprops.disabledthena[class"el_btn el_btn__disabled",href"#"][textprops.text]elsea[class"el_btn",onClickprops.onClick,href"#"][textprops.text]typealiasIconButtonPropsmsg={text:String,onClick:msg,icon:Icon.Icon}beforeIconButton:IconButtonPropsmsg->HtmlmsgbeforeIconButtonprops=leticonModifier="el_beforeIconBtn__"++Icon.toButtonModifierprops.iconina[class<|"el_beforeIconBtn "++iconModifier,onClickprops.onClick,href"#"][textprops.text]

最後は、定義したボタンの関数を呼び出して使ってみましょう。このようにコンポーネント指向でなくても、見通しが高く・再利用性と拡張性に優れたモジュールを作ることができました。ここでのポイントはMsgやボタンの状態であるdisabledなどを閉じ込めずに、プロパティとして渡すことができ、それらは使用側でハンドリングすることです。

view:Model->HtmlMsgviewmodel=article[class"ly_cont"][div[class"bl_buttonSimulation"][standardButton{onClick=StandardClick,disabled=False,text="標準ボタン"},standardButton{onClick=StandardClick,disabled=True,text="標準ボタン"},beforeIconButton{onClick=DownloadClick,icon=Icon.Download,text="ダウンロード"},p[class"bl_buttonSimulation_clickState"][text<|casemodelofStandard->"標準ボタンがクリックされました"Downloading->"ダウンロードボタンがクリックされました"UnClick->""]]]

まとめ

CSSが大の苦手な私ですが、Elmでさらに堅牢な高いコードにし、実践スピードの速さを上げ、最高の書籍を駆使することで克服の一歩を歩めています。ありがとうございます。CSSもElmもどちらもがんばっていきましょう。


Viewing all articles
Browse latest Browse all 8578

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>