2017-07-19 23 views
0

私は、データアノテーションを使用して、空ではないモデルのリストに検証を追加しようとしています。私はherehereを含むカスタム属性のいくつかの実装を試みました。リストを強制できませんが必要です

マイビュー:

<div class="form-group"> 
    @* Model has a list of ints, LocationIDs *@ 
    @Html.LabelFor(model => model.LocationIDs, htmlAttributes: new { @class = "control-label col-md-2" }) 
    <div class="col-md-10"> 
     <select class="select2 form-control" multiple id="LocationIDs" name="LocationIDs"> 
      @* Adds every possible option to select box *@ 
      @foreach (LocationModel loc in db.Locations) 
      { 
       <option value="@loc.ID">@loc.Name</option> 
      } 
     </select> 
     @Html.ValidationMessageFor(model => model.LocationIDs, "", new { @class = "text-danger" }) 
    </div> 
</div> 

モデル:

public class ClientModel 
{ 
    public int ID { get; set; } 
    [Required] // Does nothing 
    public List<int> LocationIDs { get; set; } 
} 

コントローラ:(機能的に同一)の

[HttpPost] 
[ValidateAntiForgeryToken] 
public ActionResult Create([Bind(Include = "ID,LocationIDs")] ClientModel clientModel) 
{ 
    if (ModelState.IsValid) 
    { 
     db.Clients.Add(clientModel); 
     db.SaveChanges(); 
     return RedirectToAction("Index"); 
    } 

    return View(clientModel); 
} 

一つ、私が試した属性:

[AttributeUsage(AttributeTargets.Property)] 
public sealed class CannotBeEmptyAttribute : RequiredAttribute 
{ 
    public override bool IsValid(object value) 
    { 
     var list = value as IEnumerable; 
     return list != null && list.GetEnumerator().MoveNext(); 
    } 
} 

現在、nullまたは空のリストをチェックすると、何も選択されていなくても検証が行われます。この場合、最初のオプションを含む長さ1のリストがバインドされます。


私は、コントローラが実際に長さ1のListを送っていることを確認しました。しかし、私はこの振る舞いをどのように変更するかはわかりません。私はまだこれが下のブロック引用で記述されたものかもしれないと思います。


は私が 私の問題は this answer's editで説明することができると思いますが、私はそれを解決するかどうかはわかりません。以下

抜粋:

はまた、あなたはビューであなたのリストをバインドする方法に注意する必要があります。 たとえば、あなたがこのようなビューにリストをバインドする場合:

<input name="ListName[0]" type="text" /> 
<input name="ListName[1]" type="text" /> 
<input name="ListName[2]" type="text" /> 
<input name="ListName[3]" type="text" /> 
<input name="ListName[4]" type="text" /> 

をMVCモデルバインダーは常に、あなたの リスト内のすべてのString.Emptyをする5つの要素を配置します。これがViewの仕組みであれば、 の属性はReflectionを使用して ジェネリック型パラメータを引き出し、各リスト要素を デフォルト(T)などと比較するなど、少し複雑にする必要があります。

+0

エンドユーザーが 'select'でアイテムを選択したことを検証しようとしていますか、' model.LocationIDs'にエントリがあることを検証しようとしていますか、あるいはその両方ですか? – mjwills

+0

どちらも、私は思います。クライアント側は 'select'、サーバ側は' model.LocationIDs'となります。オートマチックの検証プロパティーを100%完全に理解しているわけではありませんが、データアノテーションによって無効な値が保存されるのを防ぐことができます(必須)。 – Sinjai

+0

@mjwillsもっと明確にするために私の質問を編集するにはどうすればよいですか? – Sinjai

答えて

0

は私がゼロの選択は、実際にnullとしてバインドするために得ることができる唯一の方法はHtml.ListBoxForを使用していました(私は当初–それは最初からこの方法で行われている必要があります把握することができませんでした):私は仕事をするクライアント側の検証をもらっていないが、私は投稿します

@Html.ListBoxFor(model => model.SelectedLocations, Model.AllLocations, new { @class = "select2 form-control" }) 

それはもう一つの質問です。

1

タイプでバリデータを構築し、リストのitensのいずれかがあなたのタイプのデフォルト値と異なるかどうかを検証できます。 あなたがhereを述べた例を変更する:

public class CannotBeEmptyAttribute : ValidationAttribute 
{ 
    private const string defaultError = "'{0}' must have at least one element."; 

    public Type ListType { get; private set; } 

    protected CannotBeEmptyAttribute(Type listType) : base(defaultError) 
    { 
     this.ListType = listType; 
    } 

    public override bool IsValid(object value) 
    { 
     object defaultValue = ListType.IsValueType ? Activator.CreateInstance(ListType) : null; 

     IEnumerable list = value as IEnumerable; 

     if (list != null) 
     { 
      foreach (var item in list) 
      { 
       if(item != defaultValue) 
       { 
        return true; 
       } 
      } 
     } 
     return false;    
    } 

    public override string FormatErrorMessage(string name) 
    { 
     return String.Format(this.ErrorMessageString, name); 
    } 
} 
1

あなたCannotBeEmptyAttributeは大丈夫です。私は全く同じコードを使用しており、完全に機能します。

あなたのカスタム属性では、クライアント側の検証が提供されません。それは無効であるにもかかわらずフォームが投稿されることを意味しますが、if (ModelState.IsValid)がそれをキャッチします。デバッガを使用してIsValidfalseかどうかを確認しましたか?

入力を手動で作成するのはサーバー側の検証では問題ありませんが、クライアント側では必要なdata-属性がないために機能しません。

ここCannotBeEmptyAttributeと連携し、手動で作成したマルチセレクト入力を持つフォームの最小バージョンです:

@using (Html.BeginForm("TestPost", "Home")) 
{ 
    <select multiple="multiple" name="TestList"> 
     <option value="1">One</option> 
     <option value="2">Two</option> 
     <option value="3">Three</option> 
    </select> 
    @Html.ValidationMessage("TestList") 
    @Html.ValidationSummary() 

    <input type="submit" value="Save"/> 
} 
+0

私は、手動で行うのではなく、 'HtmlHelper'を拡張する以外に、私が望む選択ボックスを作成する方法は見当たりませんが、ソースコードを使った私の調査は、' Html.SimpleText( " words ")' 'words"はどこかの文字列に組み込まれて返されます。 – Sinjai

+0

サーバー側の検証は機能していますか? – Maetis

+0

いいえ、まだ分かりません。しかし、シードされた値に対しては*働きますが、実際にはデータベース初期化子で 'List'を' null'に設定しました。 'IsValid'は自分でフォームを送信すると真です。私はどのような値がコントローラに送られているかチェックする方法を知らない。 – Sinjai

関連する問題