2016-04-10 6 views
0

現在のプロジェクト:フォーム:すべてが検証される*ドロップダウンのための*を除き、.IsEmptyを使用していても()

  • ASP.NET 4.5.2
  • MVC 5
  • Entity Frameworkの6
  • FluentValidation

だから私は、全体の個々の要素とペアにされることを意図されている構造的に同一のテーブルある「ノート」の束を持っています少なくとも2ページと3ページ目にまとめられています。ノートを必要とするすべての要素は、単一の「サイクル」の一部です。したがって、要素は、ノートテーブルがハングしている同じテーブルのすべてのフラグメントです。たとえば、「プレゼンテーション」は、完了(イエス/ノー)ブール値とサイク​​ルテーブルの日付で構成されます。プレゼンテーションノートは、サイクルテーブル(サイクルテーブルのプライマリキーである外部キーを持ちます)からハングする2つのサイクルカラムのための別個のテーブルです。これらのノートはプレゼンテーションのためのものなので、ノートテーブル全体はPresentationNotesと呼ばれます。サイクル内には独自のNotesテーブルを持つ多くの要素があり、プロジェクト全体のすべてのNotesテーブルは構造的に同一です。

この同じ構造から、モデルとビューを抽象化することができました。私は、単一のノートテーブルごとに異なるCRUDモデルとCRUDビューを複製する必要はありませんでした。私がコントローラーに持っていたのは、各notesテーブルのモデルを取り、特定のエントリーを汎用Notesモデルのgenericエントリーに関連付けることでした。

namespace CCS.Models { 
    public class CycleNotesPresentation { 
    [Key] 
    public Guid NotesId { get; set; } 
    [DisplayName("Cycle")] 
    public Guid CycleId { get; set; } 
    [DisplayName("Comm. Type")] 
    public Guid NotesStatusId { get; set; } 
    [DisplayName("Date")] 
    [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}")] 
    public DateTime NotesDate { get; set; } 
    [DisplayName("Notes")] 
    [DataType(DataType.MultilineText)] 
    public string Notes { get; set; } 
    #region Essentials 
    //Essential DB components for each and every table. Place at end. 
    [HiddenInput, DefaultValue(true)] 
    public bool Active { get; set; } 
    [HiddenInput, Timestamp, ConcurrencyCheck] 
    public byte[] RowVersion { get; set; } 
    [HiddenInput] 
    [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}")] 
    public DateTime Recorded { get; set; } 
    [HiddenInput] 
    public DateTime Modified { get; set; } 
    [HiddenInput] 
    public string TouchedBy { get; set; } 
    #endregion 

    [ForeignKey("CycleId")] 
    public virtual Cycle Cycle { get; set; } 
    [ForeignKey("NotesStatusId")] 
    public virtual NotesStatus NotesStatus { get; set; } 
    } 
} 

あなたが見ることができるように、必ずしも抽象化モデルとビュー内にある必要はありませんここにたくさんある:

は例えば、ここでは前述のプレゼンテーションモデルです。

少なくとも作成のための抽象化されたノートモデルは、、などである。もちろん

[Validator(typeof(CreateNotesValidator))] 
public class CreateNotes { 
    public string NotesCategory { get; set; } 
    [DisplayName("Comm. Type")] 
    public string NotesStatusId { get; set; } 
    [DisplayName("Date")] 
    [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}")] 
    public DateTime NotesDate { get; set; } 
    [DisplayName("Notes")] 
    public string Notes { get; set; } 
} 

、私は他の三つのモデルがあります:表示、編集をして削除しますが、今のところこの1つだけに集中しましょう。 Createを修正できるのであれば、クライアント側の検証が必要なドロップダウンメニューを持つ唯一のEditです。

上記の違いに注意してください。NotesStatusIdフィールドは実際にはGuidの代わりに文字列です。さて、Guidを使っていれば、クライアント側の検証オプションが非常に限られていることが分かります。さらに、クライアント側の検証はまだGuidで動作していなかったので、代わりに文字列を使用してModel(したがって検証)を単純化することにしました。

元のプレゼンテーションモデルをプルすると、Guidから文字列に変換され、Notesモデルを処理してプレゼンテーションモデルに戻すと、その文字列をGuidに変換し直します。これにより、クライアント側の検証オプションを増やすことができます。

全体のプロセスのための私のコントローラのようなである:ここでは

// GET: Onboarding/CreateCycleNotesPresentation 
[HttpGet] 
public ActionResult CreateCycleNotesPresentation() { 
    var model = new CreateNotes() { 
    NotesCategory = "Presentation", 
    NotesDate = DateTime.Now 
    }; 
    ViewBag.NotesStatusId = new SelectList(db.NotesStatus.Where(x => x.Active == true), "NotesStatusId", "NotesStatusName"); 
    return PartialView("_CreateNotesPartial", model); 
} 
// POST: Onboarding/CreateCycleNotesPresentation 
[HttpPost] 
[ValidateAntiForgeryToken] 
public async Task<ActionResult> CreateCycleNotesPresentation(CreateNotes model) { 
    if(ModelState.IsValid) { 
    var id = new Guid(User.GetClaimValue("CWD-Cycle")); 
    CycleNotesPresentation cycleNotes = new CycleNotesPresentation(); 
    cycleNotes.NotesId = new Guid(); 
    cycleNotes.CycleId = id; 
    cycleNotes.NotesStatusId = new Guid(model.NotesStatusId); 
    cycleNotes.NotesDate = model.NotesDate; 
    cycleNotes.Notes = model.Notes; 
    cycleNotes.Active = true; 
    cycleNotes.Recorded = DateTime.UtcNow; 
    cycleNotes.Modified = DateTime.UtcNow; 
    cycleNotes.TouchedBy = User.Identity.GetFullNameLF(); 
    db.CycleNotesPresentation.Add(cycleNotes); 
    await db.SaveChangesAsync(); 
    return RedirectToAction("Index"); 
    } 
    model.NotesCategory = "Presentation"; 
    ViewBag.NotesStatusId = new SelectList(db.NotesStatus.Where(x => x.Active == true), "NotesStatusId", "NotesStatusName", model.NotesStatusId); 
    return PartialView("_CreateNotesPartial", model); 
} 

我々はいくつかのジューシーなビットを見ることを得る - 私はNotesCategoryエントリを追加したビューは、要素のタイトルを移入することができるようにメモが追加されています。これは最後には処理されません。

また、ページ全体を更新してPOSTを終了します。 JSONの送信が正しく行われなかったため(実際のPOSTメソッドがデータを受け取っていないため、送信がハングアップする可能性があるため)、これが最も簡単な解決策であることがわかりました。また、ページ全体が更新され、全体的なページが改善されます。それでは、それだけを残しましょう、k?

は、今最も重要なことのために:抽象ノートモデルとビューのためのバリデータ:これは私たちが取り組んでいるものではないよう

namespace CCS.Validators { 
    class NotesValidator { 
    } 
    public class CreateNotesValidator : AbstractValidator<CreateNotes> { 
    public CreateNotesValidator() { 
     RuleFor(x => x.NotesDate) 
    .NotEmpty().WithMessage("Please select a date that this communication occurred on."); 
     RuleFor(x => x.NotesStatusId) 
    .NotEmpty().NotNull().WithMessage("Please indicate what type of communication occurred."); 
     RuleFor(x => x.Notes) 
    .NotEmpty().WithMessage("Please submit notes of some kind.") 
    .Length(2, 4000).WithMessage("Please provide notes of some substantial length."); 
    } 
    } 
    public class EditNotesValidator : AbstractValidator<EditNotes> { 
    public EditNotesValidator() { 
     RuleFor(x => x.NotesDate) 
    .NotEmpty().WithMessage("Please select a date that this communication occurred on."); 
     RuleFor(x => x.NotesStatusId) 
    .NotNull().NotEmpty().NotEqual("00000000-0000-0000-0000-000000000000").Matches("^[{(]?[0-9A-F]{8}[-]?([0-9A-F]{4}[-]?){3}[0-9A-F]{12}[)}]?$").WithMessage("Please indicate what type of communication occurred."); 
     RuleFor(x => x.Notes) 
    .NotEmpty().WithMessage("Please submit notes of some kind.") 
    .Length(2, 4000).WithMessage("Please provide notes of some substantial length."); 
    } 
    } 
} 

私たちは、主に、今のEditNotesValidatorを無視することができます。

ビューには、抽象化されたノートのための単純な部分で、フォーム自体を約あなたが得ることができるようにバニラの通りです:ええ、そう

@model CCS.Models.CreateNotes 
<div class="modal-header"> 
    <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> 
    <h3 class="modal-title">Create Note for “@Model.NotesCategory”</h3> 
</div> 

@using(Html.BeginForm()) { 
    @Html.AntiForgeryToken() 
    <div class="modal-body"> 

    <fieldset> 
     @Html.LabelFor(m => Model.NotesDate, new { @class = "control-label" })<div class="input-group date">@Html.TextBoxFor(m => m.NotesDate, "{0:yyyy-MM-dd}", new { @class = "form-control date" })<span class="input-group-addon"><i class="glyphicon glyphicon-calendar"></i></span></div> 
     @Html.ValidationMessageFor(m => m.NotesDate) 
     @Html.LabelFor(m => Model.NotesStatusId, new { @class = "control-label" })@Html.DropDownList("NotesStatusId", null, "« ‹ Select › »", htmlAttributes: new { @class = "form-control" }) 
     @Html.ValidationMessageFor(m => m.NotesStatusId) 
     @Html.LabelFor(m => Model.Notes, new { @class = "control-label" })@Html.TextAreaFor(m => m.Notes, new { @class = "form-control required" }) 
     @Html.ValidationMessageFor(m => m.Notes) 
    </fieldset> 
    </div> 

    <div class="modal-footer"> 
    <span id="progress" class="text-center" style="display: none;"> 
     <img src="/images/wait.gif" alt="wait" /> 
     Wait.. 
    </span> 
    <button type="submit" value="Save" title="Save" class="btn btn-primary glyphicon glyphicon-floppy-disk"></button> 
    <button class="btn btn-warning" data-dismiss="modal">Close</button> 
    </div> 
} 
<script> 
    $("form").removeData("validator"); 
    $("form").removeData("unobtrusiveValidation"); 
    $.validator.unobtrusive.parse("form"); 
    $(function() { 
     $.fn.datepicker.defaults.format = "yyyy-mm-dd"; 
     $(".date").datepicker(); 
    }); 

</script> 

。 Dateバリデータは、期待どおりに動作します。 Notesテキストエリアは美しく検証されます。しかし、ドロップダウンメニューは完全に昼食になります。私が試してみても、それは.NotEmpty()または.NotNull()であるか、またはFluentValidationによってクライアント側で機能していると明確にフラグが付けられているものは、ドロップダウンメニューでは何も動作しません。

<select id="NotesStatusId" class="form-control" name="NotesStatusId"> 
    <option value="">« ‹ Select › »</option> 
    <option value="98e9f033-20df-e511-8265-14feb5fbeae8">Phone Call</option> 
    <option value="4899dd4d-20df-e511-8265-14feb5fbeae8">eMail</option> 
    <option value="8c073863-20df-e511-8265-14feb5fbeae8">Voice Mail</option> 
    <option value="8a13ec76-20df-e511-8265-14feb5fbeae8">Meeting</option> 
</select> 

とデフォルト« ‹ Select › »最初のオプションのためにその空の値は.NotEmpty().NotNull()完璧に動作する必要があることを意味する必要があります生のHTMLのチェックは、私がSelectListのが適切に構築取得していますことを示しています。しかし、彼らはそうではありません。日付を消去すると(上記のコントローラを参照してください)、日付フィールドとテキストエリアにのみフラグが立てられ、ドロップダウンにはフラグが立てられませんまったく。

提案?


編集1: Ehehは、おっと...今、間違ったコントローラ...固定を追加しました。


編集2: ... Bueller? ...ブエラー?


編集3:私はそれが困難な他の誰が今までFluentValidationを経由して、ドロップダウンメニューには、クライアント側の検証を行って問題がなかったしていることを信じることを発見しています。

答えて

0

これは私の後ろに来る貧しい魂のためです。具体的には、私の状況は次のとおりです。

  • モーダルダイアログでのフォーム。
  • モーダルダイアログは、サイトの多くの異なるセクションで同じ構造の複数のテーブルを取り込むために使用された汎用パーシャルを使用して起動されました。このように、単一の汎用パーシャル/フォームは、多くの異なる構造のテーブルの多くの場所で使用することができました。
  • モーダルダイアログは、ページ全体を更新するためにクローズされました。
  • これはモーダルダイアログのフォームであり、モーダル自体をリフレッシュできないため(すべてわかりません)、すべての検証はCLIENT SIDEでなければなりませんでした。これは私が持っていた最も重要な問題です。
  • のため、選択メニューの作成方法はで、クライアント側の検証は必要に応じて機能しませんでした。

私がこれを修正したのは、一部の目立ったことではなく、不適切な方法を放棄する部分でした。具体的には、ViewBags。ここで私は、ViewBagが、ドロップダウンのクライアント側の検証を行うことが、検証が必要なドロップダウン選択を入力する作業を行うと不可能になることを学びました。

私はコントローラとモデルをどのように操作していたのかというと、偶然の幸運の一部がありました。サイトのさまざまな部分に対応する複数の同じ構造のNotes表があるため、この抽象コレクションが複数のNotes表の完全なCRUDニーズを処理できるように、モデル全体、ビューおよび検証を抽象化することができました。コードの再利用、FTW!私はコントローラーの一部を抽象化することも考えています。可能であれば、それは別の日のためのものです。

は、だから私のオリジナルのポストの内容を見て、抽象化されたノートの作成と編集の部品のための私のモデルは非常に単純な足し算持っていた:あなたが見

public IList<SelectListItem> NotesStatus { get; set; } 

を、NotesStatusIdはのための外部キーでありますNotesStatusテーブル。電話、電子メール、会議、ボイスメールなど、基本的な通信の詳細を持っています。だから私は、この表からリストを作ることをモデルに伝える必要があります。

次は私のコントローラです。私はすでに特定のNotesモデルを取り込んで抽象化されたNotesモデルに詰め込んでいたので、これを拡張してViewBagに詰め込まずにドロップダウンメニューのコンテンツを含めることができました。私は以下の通りです何で私のコントローラのために上に持っていたものの比較:

[HttpGet] 
public async Task<ActionResult> CreateProspectingNotes() { 
    var model = new CreateNotes() { // We just need to set up the abstracted Notes model with a create -- no populating from the db needed. 
    NotesCategory = "Prospecting", 
    NotesDate = DateTime.Now, 
    NotesStatus = await db.NotesStatus.Where(x => x.Active).Select(x => new SelectListItem { Text = x.NotesStatusName, Value = x.NotesStatusId.ToString() }).ToListAsync() 
    }; 
    return PartialView("_CreateNotesPartial", model); 
} 

我々はビューになってしまいますSelectListのでモデルのNotesStatusの一部を充填する方法を参照してください?

我々は抽象化されたノートを起動するために持っているだけでなく、編集したいノートのテーブルからのコンテンツとそれを埋めるためだけでなく、編集は、もう少し複雑です:

[HttpGet] 
public async Task<ActionResult> EditProspectingNotes(Guid? id) { 
    ProspectingNotes prospectingNotes = await db.ProspectingNotes.FindAsync(id); // getting the specific ProspectingNotes table that is to be edited. 
    if(prospectingNotes == null) { return HttpNotFound(); } 
    EditNotes model = new EditNotes() { // Populating the abstracted Notes model with the specific ProspectingNotes model. 
    NotesCategory = "Prospecting", 
    NotesId = prospectingNotes.NotesId, 
    NotesStatusId = Convert.ToString(prospectingNotes.NotesStatusId), 
    NotesDate = prospectingNotes.NotesDate, 
    Notes = prospectingNotes.Notes, 
    NotesStatus = await db.NotesStatus.Where(x => x.Active).Select(x => new SelectListItem { Text = x.NotesStatusName, Value = x.NotesStatusId.ToString() }).ToListAsync() 
    }; 
    return PartialView("_EditNotesPartial", model); 
} 

今までに表示:我々は今、しっかりと私は信じてViewBagを呼び出す"NotesStatusId"を疎結合ではなくx => x.NotesStatusIdにバインドされているので、特に

@Html.LabelFor(m => Model.NotesStatusId, new { @class = "control-label" })@Html.DropDownListFor(x => x.NotesStatusId, new SelectList(Model.NotesStatus, "Value", "text"), "« ‹ Select › »", htmlAttributes: new { @class = "form-control" }) 
@Html.ValidationMessageFor(m => m.NotesStatusId) 

は、.DropDownList().DropDownListFor()に置き換えられクライアント側の作業全体の鍵です。 ViewBagを使用すると、すでにデフォルト値が選択されているリストをドロップダウンリストに入力するだけです。ここでは、デフォルト値をリストにバインドしてからViewModel/Controllerから直接入力します。これは強力に結合されているため、クライアント側で検証するためのものがあります。

これを完了したら、私の検証には.NotEmpty()が1つしかなく、例外を投げた.NotEmpty().NotNull()のような二重チェーンではないことが保証されていました。

こちらがお役に立てば幸いです。自分で問題が発生している場合は、これを参照する投稿を作成し、私にPMを送ってください。私は何を手助けすることができるかを見ていきます。

関連する問題