IML TODO

画像

免責事項 :この記事は批判( ハブに落ちた)への応答であり、人気のあるToDo MVCアプリケーションの例でのIMLの可能性を明らかにしています。



袖口を得た



批判は、私の前の記事で生じた議論に関連して非常にソフトな言葉です。なぜなら、それは非常に不愉快なコメントがあったphoto打に似ていたからです(写真では妄想的です)。





どうして?



コメントでは、IMLの機能の証明として「Todo MVC」を実装しようとすることを提案しましたが、今度はその結果を確認します。 まず、jsフレームワーク用に提示されたものと1つの違いがあるデモ版は、ストレージがローカルストレージではなくデータベースとソースコードを使用していることです。 実装の過程で、クライアントにすべてのロジック(地下室の計算、要素の非表示など)を構築しましたが、実際のタスクでは、IMLコードを持ち、計算方法と表示方法を知っている「ポイントワイズ」要素を更新する方が簡単です(必要な場合があります) 。



コードレビュー



今回のストーリーテリングスタイルは、あるソリューションと別のソリューションを比較するものではありません(そうでない場合、資料の量は多くなります)が、todoアプリケーションの実装時に取得されるコードのレビューです。 IMLの実装にはサーバーパーツもありますが、より客観的な比較のために解決するタスクを均等化するために、クライアントパーツのみに焦点を当てます。





何で構成されていますか



コードは3つのビューに分割されました



画像



インデックス(3つの要素で構成されます)


TODOフォームを追加


@using (Html.When(JqueryBind.Submit) .DoWithPreventDefault() .Submit(options => { options.Url = Url.Dispatcher() .Push(new AddTodoCommand { ClientId = Selector.Incoding.Cookie(CookieManager.ClientId) }); }) .OnSuccess(dsl => { dsl.WithId(containerId).Core().Trigger.Incoding(); dsl.Self().Core().Form.Reset(); }) .AsHtmlAttributes() .ToBeginTag(Html, HtmlTag.Form)) { @Html.TextBoxFor(r => r.Title, new { placeholder = "What needs to be done?", autofocus = "" }) }
      
      





注:私はすぐに、「はい、深刻ではありません。もっと多くのコードがあります。どこにでもコピーして、人々がどうするかを確認してください!!!」というスタイルのフレーズを期待しています-これに対して、C#拡張機能に直面して、IML構造をラップできる引数があります。 記事の後半では、C#拡張機能を使用して、問題に対する代替ソリューション( GibHubのリポジトリと再設計されたコード)が提供されます。

何に?




TODO追加フォーム(代替)


 @using (Html.Todo().BeginForm(setting => { setting.TargetId = containerId; setting.Routes = new { ClientId = Selector.Incoding.Cookie(CookieManager.ClientId) }; })) { @Html.TextBoxFor(r => r.Title, new { placeholder = "What needs to be done?", autofocus = "" }) }
      
      





注:コードが少なくなり、最も重要なこととして、特定のプロジェクトのニーズに合わせてメソッドを拡張(検証、送信後のリダイレクトなど)できるようになりました。

ボンネットの下
 public class BeginFormSetting { public string TargetId { get; set; } public object Routes { get; set; } } public BeginTag BeginForm(Action configure) { var setting = new BeginFormSetting(); configure(setting); var url = new UrlHelper(HttpContext.Current.Request.RequestContext); return this.helper.When(JqueryBind.Submit) .DoWithPreventDefault() .Submit(options => { options.Url = url.Dispatcher() .Push(setting.Routes); }) .OnSuccess(dsl => { dsl.WithId(setting.TargetId).Core().Trigger.Incoding(); dsl.Self().Core().Form.Reset(); }) .AsHtmlAttributes() .ToBeginTag(this.helper, HtmlTag.Form); }
      
      





注:コードはほとんどのasp.net mvc開発者に馴染みがありますが、「通常の」パラメータの代わりに、設定クラスを受け入れる匿名メソッドを渡すことに注意してください。





コンテナ


 @(Html.When(JqueryBind.InitIncoding | JqueryBind.IncChangeUrl) .Do() .AjaxGet(Url.Dispatcher() .Query(new { ClientId = Selector.Incoding.Cookie(CookieManager.ClientId), Type = Selector.Incoding.HashQueryString(r => r.Type) }) .AsJson()) .OnSuccess(dsl => { string urlTmpl = Url.Dispatcher() .Model(new GetTodoByClientQuery.Tmpl { FooterId = footerId }) .AsView("~/Views/Home/Todo_List_Tmpl.cshtml"); dsl.Self().Core().Insert.WithTemplateByUrl(urlTmpl).Html(); dsl.WithId(footerId).Core().Trigger.Incoding(); }) .AsHtmlAttributes(new { id = containerId }) .ToDiv())
      
      





何に?




コンテナ(代替方法)


 @Html.Todo().Container(setting => { setting.Id = containerId; setting.Url = Url.Dispatcher() .Query(new { ClientId = Selector.Incoding.Cookie(CookieManager.ClientId), Type = Selector.Incoding.HashQueryString(r => r.Type) }) .AsJson(); setting.Tmpl = Url.Dispatcher() .Model(new GetTodoByClientQuery.Tmpl { FooterId = footerId }) .AsView("~/Views/Home/Todo_List_Tmpl.cshtml"); setting.DependencyId = footerId; })
      
      





注:将来、ブロックuiまたは他のアクションを追加する必要がある場合、一元的に実行できるようになりました

ボンネットの下
 public class ContainerSetting { public string Id { get; set; } public string Url { get; set; } public string Tmpl { get; set; } public string DependencyId { get; set; } } public MvcHtmlString Container(Action configure) { var setting = new ContainerSetting(); configure(setting); return helper.When(JqueryBind.InitIncoding | JqueryBind.IncChangeUrl) .Do() .AjaxGet(setting.Url) .OnSuccess(dsl => { dsl.Self().Core().Insert.WithTemplateByUrl(setting.Tmpl).Html(); dsl.WithId(setting.DependencyId).Core().Trigger.Incoding(); }) .AsHtmlAttributes(new { id = setting.Id }) .ToDiv(); }
      
      







フッター


 @(Html.When(JqueryBind.None) .Do() .Direct(new FooterVm { AllCount = Selector.Jquery.Class("toggle").Length(), IsCompleted = Selector.Jquery.Class("toggle").Is(JqueryExpression.Checked), CompletedCount = Selector.Jquery.Class("toggle") .Expression(JqueryExpression.Checked) .Length(), })) .OnSuccess(dsl => { string urlTmpl = Url.Dispatcher() .Model(new TodoFooterTmplVm { ContainerId = containerId }) .AsView("~/Views/Home/Todo_Footer_Tmpl.cshtml"); dsl.Self().Core().Insert.Prepare().WithTemplateByUrl(urlTmpl).Html(); }) .AsHtmlAttributes(new { id = footerId }) .ToDiv())
      
      









Todoリストtmpl


ToDoリストを作成するためのレイアウトテンプレート

 @using (var template = Html.Incoding().Template()) { <ul> @using (var each = template.ForEach()) { @using (each.Is(r => r.Active)) { @createCheckBox(true) } @using (each.Not(r => r.Active)) { @createCheckBox(false) } <li class="@each.IsInline(r=>r.Active,"completed")"> <label>@each.For(r=>r.Title)</label> </li> </ul> }
      
      





注:ソースコードは、たとえば表示されているものよりも大きい(要素のリモートロジック)が、これはテンプレートの説明の便宜上作成されている



何に?






その他のアイテム



ボタンデル


 @(Html.When(JqueryBind.Click) .Do() .AjaxPost(Url.Dispatcher().Push(new DeleteEntityByIdCommand { Id = each.For(r => r.Id), AssemblyQualifiedName = typeof(Todo).AssemblyQualifiedName })) .OnBegin(r => { r.WithSelf(s => s.Closest(HtmlTag.Li)).Core().JQuery.Manipulation.Remove(); r.WithId(Model.FooterId).Core().Trigger.Incoding(); r.WithId(toggleAllId).Core().Trigger.None(); }) .AsHtmlAttributes(new { @class = "destroy" }) .ToButton(""))
      
      





何に?






Button Del(代替)


 @Html.Todo().Verb(setting => { setting.Url = Url.Dispatcher().Push(new DeleteEntityByIdCommand { Id = each.For(r => r.Id), AssemblyQualifiedName = typeof(Todo).AssemblyQualifiedName }); setting.OnBegin = dsl => { dsl.WithSelf(s => s.Closest(HtmlTag.Li)).Core().JQuery.Manipulation.Remove(); dsl.WithId(Model.FooterId).Core().Trigger.Incoding(); dsl.WithId(toggleAllId).Core().Trigger.None(); }; setting.Attr = new { @class = "destroy" }; })
      
      





注:OnBeginはActionを受け入れます。これにより、IMLを実装することで拡張機能を簡単に拡張できます。 (さらに例があります)



ボンネットの下
 public class VerbSetting { public string Url { get; set; } public Action<IIncodingMetaLanguageCallbackBodyDsl> OnBegin { get; set; } public Action<IIncodingMetaLanguageCallbackBodyDsl> OnSuccess { get; set; } public object Attr { get; set; } public string Content { get; set; } } public MvcHtmlString Verb(Action<VerbSetting> configure) { var setting = new VerbSetting(); configure(setting); return this.helper.When(JqueryBind.Click) .Do() .AjaxPost(setting.Url) .OnBegin(dsl => { if (setting.OnBegin != null) setting.OnBegin(dsl); }) .OnSuccess(dsl => { if (setting.OnSuccess != null) setting.OnSuccess(dsl); }) .AsHtmlAttributes(setting.Attr) .ToButton(setting.Content); }
      
      





注:Verbはいくつかのシナリオで使用するため、オプションのパラメーターを作成し、nullをチェックし、デフォルト値を設定することも簡単です





チェックボックスが完了しました


 var createCheckBox = isValue => Html.When(JqueryBind.Change) .Do() .AjaxPost(Url.Dispatcher().Push(new ToggleTodoCommand { Id = each.For(r => r.Id) })) .OnBegin(dsl => { dsl.WithSelf(r => r.Closest(HtmlTag.Li)) .Behaviors(inDsl => { inDsl.Core().JQuery.Attributes.RemoveClass("completed"); inDsl.Core().JQuery.Attributes.AddClass("completed") .If(builder => builder.Is(() => Selector.Jquery.Self())); }); dsl.WithId(Model.FooterId).Core().Trigger.Incoding(); dsl.WithId(toggleAllId).Core().Trigger.None(); }) .AsHtmlAttributes(new {@class="toggle" }) .ToCheckBox(isValue);
      
      





注:razorページ内では、匿名のC#関数またはRazorヘルパーを使用できます。これにより、同じタイプのタスクを集約できます。

何に?






タイプtodoでフィルター


 @{ const string classSelected = "selected"; var createLi = (typeOfTodo,isFirst) => Html.When(JqueryBind.InitIncoding) .Do() .Direct() .OnSuccess(dsl => { var type = Selector.Incoding.HashQueryString(r => r.Type); if (isFirst) dsl.Self().Core().JQuery.Attributes.AddClass(classSelected).If(s => s.Is(() => type == "")); dsl.Self().Core().JQuery.Attributes.AddClass(classSelected).If(s => s.Is(() => type == typeOfTodo.ToString())); }) .When(JqueryBind.Click) .Do() .Direct() .OnSuccess(dsl => { dsl.WithSelf(r => r.Closest(HtmlTag.Ul).Find(HtmlTag.A)).Core().JQuery.Attributes.RemoveClass(classSelected); dsl.Self().Core().JQuery.Attributes.AddClass(classSelected); }) .AsHtmlAttributes(new { href = "#!".AppendToHashQueryString(new { Type = typeOfTodo }) }) .ToLink(typeOfTodo.ToString()); } <li> @createLi(GetTodoByClientQuery.TypeOfTodo.All,true) </li> <li> @createLi(GetTodoByClientQuery.TypeOfTodo.Active,false) </li> <li> @createLi(GetTodoByClientQuery.TypeOfTodo.Completed,false) </li>
      
      





注:カミソリビューの一部としての匿名関数の実装の別の例

何に?






タイプtodoによるフィルター(代替方法)


 <li> @Html.Todo().LiHash(setting => { setting.IsFirst = true; setting.SelectedClass = classSelected; setting.Type = GetTodoByClientQuery.TypeOfTodo.All; }) </li>
      
      







無条件の利点!



IMLの利点は何ですか、前の記事で開示しようとしましたが、説得力がなかったので、もう一度試してみます。




All Articles